4. hét: pointerek, sztringek

Czirkos Zoltán, Nagy Gergely, Pohl László · 2023.08.27.

Gyakorlófeladatok az előadás anyagához kapcsolódóan.

1. Tömbök és függvények

Tömb eleme

Vizsgán volt

Írj függvényt, amely átvesz egy double tömböt, és visszatér a 4-es indexű elemével, ha az létezik, különben 0-val!

Megoldás

A C-ben, ha egy tömböt átadunk paraméterként, akkor csak az első elemének (nullás indexű elemének) a címe adódik át a függvénynek. Ebből az is következik, hogy a függvény nem tudja, mekkora az a tömb, vagyis azt is át kell adni, külön paraméterként. A függvény paramétereinek típusa ezért double*, a tömb elejére mutató pointer, és még egy int, amelyik pedig a tömb mérete. Hogy létezik-e a tömb 4-es indexű eleme, azt pedig ebből a méret változóból tudjuk.

double fv(double *t, int meret) {
    if (meret > 4)
        return t[4];
    else
        return 0;
}

Tömb összege

Készíts függvényt, mely egy valós számokból álló tömb elemeit összegzi!

Alig változik

Készíts függvényt, mely a paraméterben kapott egész tömbről megvizsgálja, hogy elemeinek értéke szomszédos elemek E sugaron belül helyezkednek el (különbségük nem nagyobb, mint E vagy -E) A függvény bemenő paramétere a tömbre mutató pointer, a tömb elemeinek száma, valamint az E értéke. Visszatérési értéke logikai típusú legyen, amely azt mutatja, teljesült-e a feltétel!

Legalább kettő előfordulás

Kis ZH volt

Írj függvényt, amely paraméterként vesz át egy egészekből álló tömböt, és visszaadja az első olyan tömbelem címét, amelyből legalább kettő található a tömbben! Ha nincs ilyen tömbelem, adjon vissza NULL pointert!

Megoldás
int *dupla(int *tomb, int n) {
    for (int i = 0; i < n - 1; i++)
        for (int j = i + 1; j < n; j++)
            if (tomb[i] == tomb[j])
                return tomb + i;
    return NULL;
}

Minden második

Írj függvényt, ami egy tömböt átvesz paraméterként, és hátulról indulva kiírja minden második elemét! Ügyelj arra, hogy nehogy túl/alulindexeld a tömböt!

Rendezett-e

Kis ZH volt

Írj függvényt, amely paraméterként vesz át egy valós értékekből álló tömböt, melyről biztosan tudjuk, hogy elemei különbözőek! A függvény ellenőrizze, hogy a tömb rendezett-e (akár növekvő, akár csökkenő sorrendben; a feltételezett rendezettség iránya az első két tömbelem vizsgálatával eldönthető). Ha a tömb nem rendezett, a függvény adja vissza az első olyan tömbelem címét, amelyik elrontja a rendezettséget! Ha a tömb rendezett, adjon vissza NULL pointert! Pl. be: {-8.11, -5.3, 0.1, 2.5, 1.4, 6.9, 12.0, 5.7}, a visszaadott érték az 1.4-et tartalmazó tömbelem címe. Pl. be: {7, 1, 2, 3, 4, 5}, a visszaadott érték a 2-t tartalmazó tömbelem címe.

Megoldás
double *rendezette(double *tomb, int n) {
    if (n < 2) return NULL;
    bool novekvo = tomb[0] < tomb[1];
    if (novekvo) {
        for (int i = 2; i < n; i++) {
            if (tomb[i - 1] > tomb[i])
                return tomb + i;
        }
    } else {
        for (int i = 2; i < n; i++) {
            if (tomb[i - 1] < tomb[i])
                return tomb + i;
        }
    }
    return NULL;
}

Legnagyobb

Készíts függvényt, amely paraméterként vesz át egy egész számokból álló tömböt, és visszaadja a tömb legnagyobb elemének indexét! Egészítsd ki teljes programmá, amely kiírja a legnagyobb tömbelemet! (A kiírást ne a maximumkereső függvény végezze!)

Alakítsd át a programot úgy, hogy ne a legnagyobb elem indexét, hanem annak memóriacímét adja vissza a függvény!

Megoldás
#include <stdio.h>

int max(int *t, int elemszam) {
    int maxidx = 0;      /* Feltételezzük, hogy van legalább egy eleme */
    for (int i = 1; i < elemszam; ++i)
        if (t[i] > t[maxidx])
            maxidx = i;  /* Ha találunk nagyobbat, megjegyezzük az indexét */
    return maxidx;       /* Visszaadjuk a maximumot */
}

int* maxcim(int *t, int elemszam) {
    int maxidx = 0;      /* Feltételezzük, hogy van legalább egy eleme */
    for (int i = 1; i < elemszam; ++i)
        if (t[i] > t[maxidx])
            maxidx = i;  /* Ha találunk nagyobbat, megjegyezzük az indexét */
    return &t[maxidx];   /* Visszaadjuk a címét. return t+maxidx is jó. */
}

int main(void) {
    int tomb[5] = {1,5,3,9,8};
    printf("%d\n", tomb[max(tomb, 5)]);
    printf("%d\n", *maxcim(tomb, 5));

    return 0;
}

Alakítsd át úgy is a programot, hogy a ciklusok tömbindexek helyett pointerekkel dolgozzanak, az előadáson bemutatott módon.

Megoldás
int max(int *t, int elemszam) {
    int *max = t;
    for (int *p = t+1; p != t+elemszam; ++p)
        if (*p > *max)
            max = p;
    return *max;
}

Legkisebb és legnagyobb

Kis ZH volt

Írj függvényt, amely paraméterként vesz át egy egészekből álló tömböt! A függvény adja vissza címükkel átvett változókban a tömb legkisebb és legnagyobb elemének indexét! Ha több egyforma érték a legkisebb/legnagyobb, akkor ezek közül bármelyik indexét visszaadhatja.

Megoldás

A minimum- és maximumkeresésben használt indexhez illik lokális változót használni, és csak a függvény végén betenni az eredményt a cím szerint átvett változókba. Ennek okai:

  • Egyszerűbb a kód, nincs tele *pmin és *pmax kifejezésekkel.
  • Áttekinthetőbb is.
  • Gyorsabb is, mivel a keresős ciklus lokális változókkal dolgozik, nem pedig indirekten elér változókkal.
void minmax(int *tomb, int n, int *pmin, int *pmax) {
    int min = 0, max = 0;
    for (int i = 1; i < n; i++) {
        if (tomb[i] < tomb[min]) min = i;
        if (tomb[i] > tomb[max]) max = i;
    }
    *pmin = min;
    *pmax = max;
}

Súlypont

Készíts struktúratípust, amely alkalmas egy térbeli pont koordinátáinak eltárolására (x, y, z koordinálta). Írj függvényt, amely átvesz egy térbeli pontokból álló tömböt, és visszaadja a pontok súlypontját (azaz azt a pontot, amelynek a koordiátáit a bemenő pontok megfelelő koordinátáinak átlagai)! Próbáld ki a függvényt teljes programmá kiegészítve!

2. Tömbök és összetett függvények

Hány egyedi elem van?

Készíts függvényt, mely egy adott tömbben megszámolja, hogy hány olyan elem van, amely csak egyszer fordul elő! Pl. a { 2, 7, 5, 8, 9, 5, 7, 5, 5, 3 } tömbre a visszatérési érték legyen 4, mert a { 2, 3, 8, 9 } számok mind csak egyszer szerepeltek! (Használhatod a Legalább kettő feladatban elkészült függvényt.)

A leggyakoribb elem

Készíts függvényt, mely meghatározza egy adott (véletlen számokkal feltöltött) tömbben, hogy melyik értékből található benne a legtöbb! Pl. ha a tömb elemei { 2, 7, 5, 8, 9, 5, 7, 5, 5, 3 }, akkor a függvény visszatérési értéke legyen 5, mivel az a leggyakoribb elem.

Rendezettség vizsgálata

Kis ZH volt

Írj egy függvényt, amelyik egy double számokból álló tömböt vesz át paraméterként. A függvény térjen vissza egy felsorolt típussal, amelynek lehetséges értékei: csokkeno , ha a tömbben lévő számsorozat szigorúan monoton csökken; novekvo, ha szigorúan monoton nő; osszevissza, ha egyik sem igaz rá. Írj egy programrészt, amelyik definiál egy tömböt, és kiírja, hogy „növekvő”, ha a tömbben lévő számok szigmon növekvő sorban vannak. Pl. [3 2.1 0.9] → csokkeno, [3 4 2 9 5] → osszevissza, [3 4.65 9 11] → novekvo.

Megoldás
#include <stdio.h>
#include <stdbool.h>

/* nem lenne muszaj typedefelni amugy */
typedef enum { osszevissza, novekvo, csokkeno } SzigMon;

/* a feladat nem definialja azt, mi a helyzet a 0 es 1 elemu
   tombokre (amik szigmon novekvoek es csokkenoek is :D) */

SzigMon vizsgal(double *t, int meret) {
    bool nov = true, csokk = true; /* egyelore barmelyik lehet */

    /* vigyazni a tulindexelesre! i+1, szoval itt i<meret-1*/
    for (int i = 0; i < meret - 1; i++)
        /* ha nem igaz, hogy kisebb a kovetkezonel */
        if (!(t[i] < t[i + 1]))
            /* akkor ez novekvo nem lehet */
            nov = false;

    /* ugyanaz a logika */
    for (int i = 0; i < meret - 1; i++)
        if (!(t[i] > t[i + 1]))
            csokk = false;

    /* hacsak nem 0 vagy 1 elemu a tomb, akkor ez megfelel */
    if (csokk) return csokkeno;
    if (nov) return novekvo;
    return osszevissza;
}

int main(void) {
    double t1[5] = {5, 9, 1, 3, 45};
    double t2[5] = {1, 2, 3, 4, 5};
    double t3[5] = {9, 8, 7, 6, 5};

    if (vizsgal(t1, 5) == osszevissza) printf("t1 osszevissza\n");
    if (vizsgal(t2, 5) == novekvo) printf("t2 novekvo\n");
    if (vizsgal(t3, 5) == csokkeno) printf("t3 csokkeno\n");

    return 0;
}

Párosak vagy negatívak?

Kis ZH volt

Írj egy függvényt, amelyik egy egész számokból álló tömböt vesz át paraméterként. A függvény térjen vissza az alábbi felsorolt típusból valamelyik értékkel: parosak, negativak, mindketto, egyiksem, ha a tömbben páros az összes szám, negatív az összes, illetve ha mindkét tulajdonság, vagy egyik tulajdonság sem érvényes rájuk. Írj programot, amelyik egy példaként definiált 100 elemű tömbre meghívja a függvényt, és kiírja, hogy „párosak”, ha érvényes rá ez a tulajdonság.

  • [3 4 5] → egyiksem
  • [-2 -4 -6] → mindketto
  • [4 6 10] → parosak
Megoldás
#include <stdio.h>
#include <stdbool.h>

typedef enum { parosak, negativak, mindketto, egyiksem } Tulajdonsagok;

Tulajdonsagok vizsgal(int *t, int meret) {
    /* a megoldas gondolata, pl. parosakra:
     * 1) tetelezzuk fel, hogy az osszes szam paros.
     * 2) nezzuk vegig a tombot
     *    2a) ha talalunk egy nem paros szamot...
     *    2b) ... akkor nem igaz az, hogy mind parosak. */

    /* 1 */
    bool prsk = true;
    bool ngtvk = true;
    /* 2 */
    for (int i = 0; i < meret; ++i) {
        if (t[i] % 2 != 0)      /* <- 2a */
            prsk = false;       /* <- 2b */
        if (t[i] >= 0)
            ngtvk = false;
    }

    if (prsk && ngtvk)
        return mindketto;
    if (prsk)           /* ... de nem ngtvk */
        return parosak;
    if (ngtvk)          /* ... de nem prsk */
        return negativak;
    return egyiksem;    /* mar csak ez lehet. */
}

int main(void) {
    int t[5] = {4, 6, 8, 10, 12};

    if (vizsgal(t, 5) == parosak)
        printf("parosak");

    return 0;
}

3. "Bittömbök" kezelése

24×21-es képecskék

Egy játékhoz, amit írunk, szükség vagy 24×21 fekete/fehér pontból álló kis képecskékre. Mivel ezekből rengeteg lesz, kitaláljuk, hogy a fekete/fehér jelleg miatt egy bit is tárolhat egy pontot, így az egy kép által lefoglalt memória (innentől feltételezve a 8 bites char-t) 3×21=63 bájtot foglal csak el a memóriából. Feladat: írni három függvényt, amelyek a következőeket tudják:

  • Kirajzolni pontokból és csillagokból egy ilyen képecskét.
  • Fehérre állítani egy pontot.
  • Feketére állítani egy pontot.
Megoldás
#include <stdio.h>

typedef unsigned char kep[63];

// Az elso feladatresz megoldasa: egy kepecske kirajzolasa
void kirajzol(kep k) {
    for (int y = 0; y < 21; y++) {
        for (int x = 0; x < 24; x++) {
            /* annyival shift, utana legalso bit */
            /* (szóköz helyett .-ot használtam most) */
            printf("%c", (k[y * 3 + x / 8] >> (7 - x % 8)) & 1 ? '*' : '.');
        }
        printf("\n");
    }
    printf("---\n");
}

// A masodik feladatresz: adott pontot feherre allit
void feher(kep k, int x, int y) {
    /* keppont aktiv: bitenkenti VAGY */
    k[y * 3 + x / 8] = k[y * 3 + x / 8] | (1 << (7 - x % 8));
}

// A harmadik feladatresz: adott pontot feketere allit
void fekete(kep k, int x, int y) {
    /* keppont ki: bitenkenti ES a negalttal */
    k[y * 3 + x / 8] = k[y * 3 + x / 8] & ~(1 << (7 - x % 8));
}

int main(void) {
    kep k;

    for (int y = 0; y < 21; y++)
        for (int x = 0; x < 24; x++)
            fekete(k, x, y);
    kirajzol(k);
    for (int x = 0; x < 24; x++)
        feher(k, x, 10);
    kirajzol(k);
    for (int y = 0; y < 21; y++)
        feher(k, 5, y);
    kirajzol(k);

    return 0;
}

Megoldható kétdimenziós tömbbel is, kep[3][21]. Az egy bájton belüli képpond sorrend is tetszőleges; vagy mindenhol 7-x%8, vagy mindenhol simán x%8.

Minden szám megfordítása

Kis ZH volt

Írj C programot, amelyik definiál egy 1000 elemű, bájtokból álló tömböt. A program fordítsa meg az egyes bájtokban a biteket; a 7. helyiértékű cseréljen helyet a 0. helyiértékűvel, a 6. helyiértékű az 1-essel stb. (Feltételezzük, hogy a bájtok 8 bitesek. A tömb számokkal feltöltésével nem kell foglalkozni.) A program végezetül írja ki binárisan a tömb 0. elemét. A megfordításra példa:

76543210
10110010    bemenet
01001101    kimenet
Megoldás
#include <stdio.h>

int main(void) {
    unsigned char t[1000];

    /* egyet most itt inicializalok, hogy lassam az eredmenyt,
     * de a feladat nem keri */
    t[0] = 0xd7;

    /* kiiras, csak hogy a csere elott is lassuk */
    for (int i = 7; i >= 0; i--)
        putchar((t[0] >> i) & 1 ? '1' : '0');
    printf("\n");

    /* ez a MEGOLDAS LENYEGE */
    for (int i = 0; i < 1000; i++) {
        unsigned char uj = 0;

        /* amit a regi szambol kishiftelunk jobbRA,
         * azt az ujba beshifteljuk jobbROL */
        for (int j = 0; j < 8; j++) {
            int x = t[i] & 1;   /* ez kiveszi az also bitet */
            t[i] >>= 1;         /* a regit shifteli */
            uj = uj << 1 | x;       /* ez az ujat balra shifteli,
                                         es az ujat berakja */
        }
        t[i] = uj;
    }

    /* ez csak copypaste, a feladat egyszer keri */
    for (int i = 7; i >= 0; i--)
        putchar((t[0] >> i) & 1 ? '1' : '0');
    printf("\n");

    return 0;
}

Bitek léptetése

Kis ZH volt

Írj egy C programot, amelyik 100 elemű, bájtokból álló tömböt léptet egy bittel jobbra! A számokból jobbra kicsúszó bit jöjjön be mindig a következő számba balról. Az utolsó szám legalsó helyiértékéből kicsúszó bit pedig kerüljön az első szám legfelső helyiértékébe. (Feltételezzük, hogy a bájtok 8 bitesek. A tömb számokkal feltöltésével nem kell foglalkozni.) Például:

76543210 76543210 ... 76543210 76543210
01001010 11111101     01011110 00001101    bemenet
10100101 01111110 ... 10101111 00000110    kimenet
Megoldás
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    unsigned char t[100];

    /* ezt nem kerte a feladat, csak hogy ne legyen inicializalatlan */
    for (int i = 0; i < 100; ++i)
        t[i] = rand() % 256;

    /* ezt se kerte, csak hogy latszodjon az eredmeny */
    for (int i = 7; i >= 0; i--)
        putchar((t[0] >> i) & 1 ? '1' : '0');
    printf(" ... ");
    for (int i = 7; i >= 0; i--)
        putchar((t[99] >> i) & 1 ? '1' : '0');
    printf("\n");

    /* MEGOLDASA a feladatnak: tulajdonkepp ez a ciklus. */
    /* utolso szam utolso bitje,
     * mert ez csuszik be az elso szamba; mar most kivesszuk,
     * hogy lent, amikor az atvitelt "becsusztatjuk" az
     * if (atvitel) resznel, ott mar a megfelelo erteket
     * tartalmazza */
    int atvitel = t[99] & 1;
    for (int i = 0; i < 100; i++) {
        int uj_atvitel = t[i] & 1; /* a kovetkezo szamhoz - ez a kicsuszo bit */
        t[i] >>= 1;                /* ezzel csuszik az egesz */
        if (atvitel == 1)          /* ha volt "atvitel", akkor azt berakjuk a legfelsobe */
            t[i] |= 1 << 7;
        atvitel = uj_atvitel;
    }

    /* kiirom az eredmenyt; a feladat nem kerte */
    for (int i = 7; i >= 0; i--)
        putchar((t[0] >> i) & 1 ? '1' : '0');
    printf(" ... ");
    for (int i = 7; i >= 0; i--)
        putchar((t[99] >> i) & 1 ? '1' : '0');
    printf("\n");

    return 0;
}

4. Sztringek

Sztringek, mint karaktertömbök

Hozzunk létre egy sztringet! Változtassunk meg benne néhány karaktert! Írjunk ciklust, amelyik megszámolja az 'l' betűket a sztringben!

Megoldás
#include <stdio.h>

int main(void) {
    char sz[20] = "Hello, vilag!";

    printf("|%s|\n", sz);
    sz[0] = 'h';              /* sztring: "idezojel", karakter: 'h' aposztrof! */
    printf("|%s|\n", sz);
    sz[7] = '\0';             /* 0 is lehetne, ugyanaz. */
    printf("|%s|\n", sz);

    /* tekintsünk a sztringre, mint tömbre. hol van vége?
       ahol sz[i]==0, nem pedig i=19-nél, ami a tömb méretéből adódna! */
    int db = 0;
    for (int i = 0; sz[i] != '\0'; ++i)
        if (sz[i] == 'l')
            db += 1;
    printf("[%s] sztring %d darab l betűt tartalmaz.\n", sz, db);

    return 0;
}

Üdvözlés

Készíts programot, amely bekéri a felhasználó nevét, majd üdvözli őt a nevén szólítva!

Hány szóköz?

Készíts programot, mely bekér egy mondatot, majd
a.) megszámolja és kiírja, hogy a mondatban hány szóköz található.
b.) kiírja a mondatot szóközök nélkül.

Kisbetűk I.

Készíts függvényt (numLower), ami megkap egy stringre mutató pointert, és visszaadja az adott szövegben található kisbetűk számát. (Ehhez használható a ctype.h islower() függvénye is.)

Kisbetűk II.

Írj C függvényt, amely egy nullával terminált sztringben kicseréli az angol abécé nagybetűit a nekik megfelelő kisbetűkre. Ha a bemeneti sztring "Hello Vilag", módosítsa azt "hello vilag"-ra. (Tipp: A megoldáshoz a ctype.h könyvtári függvényei használhatóak.)

Hogyan kell módosítani a függvényt, hogy nagybetűkre cseréljen?

Felülírás és csere

Írj függvényt, amely az első paraméterében kapott sztringben megkeresi a második paraméterében adott karakter előfordulásait, és felülírja azokat a harmadik paraméterében adott karakterrel! Pl. "alma", 'a', 'e' → "elme". A függvény visszatérési értéke a kicserélt karakterek száma legyen.

Írj függvényt, amely szintén egy sztringet és egy karakterpárost kap, de ez ne felülírja az első előfordulásait a másodikkal, hanem cserélje meg őket! Pl. 'a', 'e' jelentse azt, hogy 'a'-t 'e'-re kell cserélni, 'e'-t pedig 'a'-ra. Hogyan lehet ezt megoldani az előző függvény felhasználásával?

Squeeze

Írj olyan "squeeze" függvényt, amely az első paraméterben megadott sztringből az összes olyan karaktert törli, amelyik szerepel a második paraméterben megadott sztringben. Például "megadott sztring", "gt" paraméterekkel meghívva a függvényt az első paraméter így módosul: "meado szrin".

Pontosan egyszer

Írj programot, amely beolvas egy sztringet, és megállapítja, hogy vannak-e benne olyan karakterek, amelyek pontosan egyszer fordulnak elő. A program írja ki ezeket a karaktereket, ha pedig nincsenek a sztringben egyedi karakterek, akkor közölje a felhasználóval!

5. Könyvtári sztringkezelő függvények

Decimális

Írj olyan int dec_to_int(char *s) függvényt, amelyik a megadott számjegyekből álló sztringet a neki megfelelő egész értékké alakít (tízes számrendszer szerint)! Pl. dec_to_int("256") visszatérési értéke 256. Oldd meg a feladatot a sscanf() segítségével és anélkül is!

Hexadecimális

Írj olyan int hexa_to_int(char *s) függvényt, amelyik a megadott hexadecimális számjegyekből álló sztringet a neki megfelelő egész értékké alakít! Pl. hexa_to_int("1ef") visszatérési értéke 495. Oldd meg a feladatot a sscanf() segítségével és anélkül is!

Legalább kettő – sztringre

Kis ZH volt

Írj függvényt, amely paraméterként vesz át egy sztringet, és visszaadja az első olyan karakter címét, amelyből legalább kettő található a sztringben! Ha nincs ilyen karakter, adjon vissza NULL pointert!

Megoldás
char *duplas(char *string) {
    for (int i = 0; string[i] != '\0'; i++)
        for (int j = i+1; string[j] != '\0'; j++)
            if (string[i] == string[j])
                return string+i;
    return NULL;
}

Része-e?

Írj egy függvényt, amely egy adott sztringben megkeresi egy másik sztring legutolsó előfordulását, és visszaadja annak pozícióját, illetve -1-et, ha nem található. A megoldáshoz ne használd a könyvtári strrstr() függvényt! Például "abcdabce"-ben keressük "abc"-t, a visszatérési érték 4, a színnel jelölt előfordulás miatt.

Megoldás

A következő módon bontható fel ezt részekre:

  • Kell egy függvény, amelyik megmondja, hogy egy adott sztring elején szerepel-e egy másik sztring. Ezt fogom lefuttatni a különböző részein az eredeti sztringeknek (visszafelé). Szemfülesek az strncmp-t használhatják erre.
  • Kell egy függvény, amelyik egy sztring hosszát megmondja (de jó a gyári strlen is), mivel
  • visszafelé futtatok egy ciklust, és nézem, hogy megtalálom-e valahol a szénakazal végén a tűt.
  • Ahol először megtalálom, azzal az indexszel vissza is térhetek; ha sehol nem találtam meg, akkor -1-gyel.
#include <stdio.h>
#include <stdbool.h>

bool igy_kezdodik(char *mi, char *hogyan) {
    int i;

    /* amig egyiknek sincs vege, es egyeznek a betuk, kov. karakter */
    i=0;
    while (hogyan[i]!='\0' && mi[i]!='\0' && hogyan[i]==mi[i])
        i++;
    /* ha a hogyan string vegere ertunk, akkor eddig tuti megegyezett
       a mi-vel */
    return hogyan[i]=='\0';
}

int hossz(char *str) {
    int i = 0;
    while (str[i]!='\0') i++;
    return i;
}

int utolso_elofordulas(char *szenakazal, char *tu) {
    int h;

    h=hossz(szenakazal);
    h-=hossz(tu);   /* ennel csak elorebb lehet */
    while (h>=0 && !igy_kezdodik(szenakazal+h, tu))
        h--;
    /* ha ertelmes index van, akkor azzal terunk vissza */
    if (h>=0)
        return h;
    /* amugy -1 */
    return -1;
}

int main(void) {
    printf("%d\n", utolso_elofordulas("almafa, eperfa", "fa"));
    printf("%d\n", utolso_elofordulas("almafa, eperfa", "a"));
    printf("%d\n", utolso_elofordulas("almafa, eperfa", "kortefa"));

    return 0;
}

Az első függvénynek az is jó megoldás lenne, ha az összehasonlítandó karakterek számát paraméterként kapja; olyankor nem kellene figyelnie a lezáró nullákra.

strcat()

Írjunk függvényt, amelyik egyik sztring végére másol egy másikat, vagyis hozzáfűzi a paraméterként kapott első sztringhez a másodikat! (Ezt csinálja a könyvtári strcat() függvény is.)

Megoldás
#include <stdio.h>

void sztringhozzafuz(char *mihez, char *honnan) {
    int folytat, i;

    i = 0;
    while (mihez[i] != '\0')
        i++;      /* ezzel megkeressuk, az elobbinek hol van vege */
    folytat = i;  /* es oda masoljuk a masikat, folytatolagosan */

    i = 0;        /* nezzuk a masik sztringet az elejetol */
    while (honnan[i] != '\0') {
        mihez[folytat] = honnan[i];
        i++;
        folytat++;
    }
    mihez[folytat] = '\0';     /* lezaro nulla eddig nem - most megtesszuk. */
}

/* peldak, hogyan kell meghivni a fuggvenyeket */
int main(void) {
    /* mit a mizujs[]? azt, hogy a fordito kitalalja a tomb meretet. */
    /* a hello[] sztringhez hozzafuzunk, ezert az nagyobb kell legyen. */
    char hello[50] = "Hello, vilag!",
         mizujs[] = "Mizujs?";

    printf("Sztring: [%s] es [%s]\n", hello, mizujs);
    sztringhozzafuz(hello, " ");
    sztringhozzafuz(hello, mizujs);
    printf("Osszefuzve: [%s].\n", hello);

    return 0;
}

strlcat()

Kis ZH volt

Írj egy függvényt (paraméterei: cél, forrás, cél tömb mérete), amelyik egy cél sztring (1. paraméter) végére hozzáfűz egy forrás sztringet (2. paraméter); figyelembe véve azt, hogy a cél tömb maximális mérete adott (3. paraméter), amelybe már a lezáró nullának is bele kell férnie. Mindkét helyen eredendően is 0-val lezárt sztring van. Ha az összefűzött sztring nem fér el a cél helyen, akkor le kell vágnia a függvénynek – de nullával mindig legyen lezárva. Írj programrészt, amelyben bemutatod a függvény használatát. A string.h függvényei NEM használhatóak.

Megoldás
Az strlcat() függvény működése

Rendes helyeken ilyen gyárilag szokott lenni, strlcat vagy g_strlcat néven. Az egésznek az előnye, hogy a cél puffer méretét kell megadni a harmadik paraméterben, ami statikus tömb esetén egy sima sizeof. Nem kell levonni 1-et a lezáró 0 miatt, semmi ilyesmi, pontosan a méretet várja.

#include <stdio.h>

void strlcat(char *cel, char *forras, int meret) {
    int celmeret, forrasidx;

    celmeret=0;
    while (cel[celmeret]!=0)
        ++celmeret;

    forrasidx=0;
    while (forras[forrasidx]!=0 && celmeret+forrasidx<meret-1) {
        cel[celmeret+forrasidx]=forras[forrasidx];
        forrasidx++;
    }

    /* akarmiert is lett vege, lezaro 0. */
    cel[celmeret+forrasidx]=0;
}

int main(void) {
    char cel[6]="alma";
    char cel2[9]="alma";
    strlcat(cel, "le", sizeof(cel));
    strlcat(cel2, "le", sizeof(cel2));
    printf("[%s]\n", cel);
    printf("[%s]\n", cel2);

    return 0;
}

Összefűzés

Kis ZH volt

Írj függvényt, amely paraméterként vesz át egy cél sztringet, továbbá két másik sztringet és egy elválasztó karaktert! Másolja be a cél sztringbe a másik két sztringet úgy, hogy közéjük az elválasztó karaktert teszi.

Írj főprogramot, amelyben egy példával bemutatod a függvény használatát! A beépített sztringkezelő függvények nem használhatóak!

Példa paraméterek: „alma” és „körte”, továbbá „;”
Példa eredmény: „alma;körte”

Megoldás
#include <stdio.h>

void osszefuz(char *ide, char *egyik, char *masik, char koze) {
   int pos=0;
   for (int i=0; egyik[i]!='\0'; ++i)
      ide[pos++]=egyik[i];
   ide[pos++]=koze;

   for (int i=0; masik[i]!='\0'; ++i)
      ide[pos++]=masik[i];
   ide[pos++]='\0';
}

int main(void) {
   char kesz[20];

   osszefuz(kesz, "alma", "korte", ';');
   printf("%s\n", kesz);

   return 0;
}

Sztringek szétválasztása

Kis ZH volt

Írj egy függvényt, amely paraméterként vesz át egy bemeneti sztringet és egy elválasztó karaktert! Legyen még két további paramétere, amelyekbe az eredményt írja. Vágja ketté a függvény a sztringet az első elválasztó karakternél: az eleje menjen az egyik eredmény sztringbe, másik pedig a másikba!

Írj főprogramot, amelyben egy példával bemutatod a függvény használatát! A beépített sztringkezelő függvények nem használhatóak.

Példa paraméterek: „alma;körte” és „;”
Példa eredmény: „alma” és „körte”

Megoldás
#include <stdio.h>

void szeletel(char *be, char elvalaszto, char *egyik, char *masik) {
    int x = 0;
    int pos = 0;
    while (be[x] != elvalaszto)
        egyik[pos++] = be[x++];
    egyik[pos] = '\0';
 
    x++;
    pos = 0;
    while (be[x] != '\0')
        masik[pos++] = be[x++];
    masik[pos] = 0;
}

int main(void) {
    char bal[20], jobb[20];

    szeletel("alma;korte", ';', bal, jobb);
    printf("[%s] es [%s]\n", bal, jobb);

    return 0;
}

strstr()

Írj függvényt, amely két sztringet vesz át paraméterként, és az elsőben megkeresi a második első előfordulását! Ha megtalálja, adja vissza a megtalált szöveg első karakterének címét, ha nincs benne, akkor NULL pointert! A megoldáshoz nem használhatsz könyvtári függvényt. (A feladat a string.h-ban található strstr függvény saját megvalósítása.) Egészítsd ki teljes programmá, a program az "Indul a kutya s a tyúk aludni." mondatban keresse meg a "kutya" szót! (A függvény a kis és nagybetűket tekintse különbözőnek!)

Megoldás
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

char* mystrstr(char* hol, char* mit) {
    for (int i = 0; hol[i]!='\0'; i++) {
        bool talalt = true;
        for (int j=0; hol[i+j] && mit[j] && talalt; j++)
            if (hol[i+j]!=mit[j])
                talalt = false;
        if (talalt && mit[j]=='\0')
            return &hol[i];
    }
    return NULL;
}

int main(void) {
    char s1[] = "Indul a kutya s a tyúk aludni.";
    char* pos1 = mystrstr(s1, "kutya");
    if (pos1 != NULL)
        printf("Pozíció: %d\n", (int) (pos1-s1));
    else
        printf("NULL - nincs benne\n");

    char s2[] = "Indul a kuty";
    char* pos2 = mystrstr(s2, "kutya");
    if (pos2 != NULL)
        printf("Pozíció: %d\n", (int) (pos2-s2));
    else
        printf("NULL - nincs benne\n");

    return 0;
}

6. Összetett sztringes függvények

Névelő

Készíts programot, mely adott sztringben megszámolja, hányszor fordul elő az „a” névelő. A névelő lehet mondat elején, de végén nem, viszont vessző állhat előtte is és utána is, egyébként szóköz karakterek határolják.

Caesar kódolás

A gyakorlaton volt egy olyan példa, amelyik karaktereket képes bekódolni a→b, b→c, c→d stb kódolással. Írj egy függvényt, amelyiknek megadható a kódolandó karakter, és a→d kódolást használ. Javítsd úgy az órán tárgyalt függvényt, hogy csak a kisbetűket kódolja, más karaktereket hagyjon változatlanul. Figyelj arra is, hogy a programkódban ne legyenek mágikus értékek (pl. 26, mint az abc betűinek száma).

Megoldás
#include <stdio.h>

/* Ez bekodol egy karaktert, a kulcs szerint. */
char kodol(char mit, char kulcs) {
    char delta = kulcs - 'a';
    if (mit >= 'a' && mit <= 'z') {
        /* eltolas */
        mit = mit + delta;
        /* tulcsuszott a z-n? */
        if (mit > 'z')
            /* akkor vissza kell menni annyit, hogy ujra az abecen
               belul legyunk. az 'annyit' erteke 'z'-'a'+1 lepes,
               nem pedig 'z'-'a'! az utobbi a ket karakter kozotti
               tavolsag, az elso pedig az a szam, amely az osszes
               letezo betuk szamat mutatja! */
            mit = mit - ('z' - 'a' + 1);
    }
    return mit;
}

char dekodol(char mit, char kulcs) {
    char delta = kulcs - 'a';
    if (mit >= 'a' && mit <= 'z') {
        mit = mit - delta;
        if (mit < 'a')
            mit = mit + ('z' - 'a' + 1);
    }
    return mit;
}


int main(void) {
    char szoveg[] = "hello, world!";

    /* az abc kiirasa */
    for (int i = 'a'; i <= 'z'; ++i)
        printf("%c", i);
    printf("\n");
    for (int i = 'a'; i <= 'z'; ++i)
        printf("%c", kodol(i, 'd'));
    printf("\n");

    /* szoveg kodolasa */
    for (int i = 0; szoveg[i] != 0; ++i)
        szoveg[i] = kodol(szoveg[i], 'd');
    printf("[%s]\n", szoveg);

    /* szoveg dekodolasa */
    for (int i = 0; szoveg[i] != 0; ++i)
        szoveg[i] = dekodol(szoveg[i], 'd');
    printf("[%s]\n", szoveg);

    return 0;
}

Karakterek törlése

Kis ZH volt

Írj függvényt, amely átvesz paraméterként egy módosítandó sztringet és még egy karaktert. Alakítsa át úgy a sztringet úgy, hogy a megadott karaktert törölje a sztring elejéről és a végéről is! Mindkét oldalon lehet több is, vagy akár semennyi. A belsejében viszont tudjuk, hogy nincsen.

Írj főprogramot, amelyben egy példával bemutatod a függvény használatát! A beépített sztringkezelő függvények nem használhatóak.

Példa bemenet: „xxxHello hallo elektor kalandorxxxx” és az „x” karakter

Példa kimenet: „Hello hallo elektor kalandor”

Megoldás
#include <stdio.h>

void sztring_trim(char *str, char mit) {
    int eleje = 0;
    while (str[eleje] == mit)
        eleje++;
    int pos = 0;
    for (int i = eleje; str[i] != mit && str[i] != '\0'; i++)
        str[pos++] = str[i];
    str[pos] = '\0';
}

int main(void) {
    char szoveg[] = "xxxHello hallo elektor kalandorxxxx";

    sztring_trim(szoveg, 'x');
    printf("[%s]\n", szoveg);

    return 0;
}

Bevezető és lezáró karakterek

Kis ZH volt

Írj függvényt, amely paraméterként átvesz egy cél sztringet, továbbá egy forrás sztringet, egy karaktert és egy darabszámot! Másolja át a cél sztringbe a forrást úgy, hogy elé és mögé a megadott karakterből a megadott darabszámút tegye. Ezen kívül a szóközöket is cserélje ki a megadott karakterre.

Írj főprogramot, amelyben egy példával bemutatod a függvény használatát! A beépített sztringkezelő függvények nem használhatóak.

Példa bemenet: „Hello hallo elektor kalandor”, továbbá az „x” karakter és 3

Példa kimenet: „xxxHelloxhalloxelektorxkalandorxxx”

Megoldás
#include <stdio.h>

void strelemoge(char *ide, char *mit, char kar, int db) {
    int pos = 0;
    
    for (int x = 0; x < db; ++x)
        ide[pos++] = kar;

    for (int x = 0; mit[x] != '\0'; ++x)
        ide[pos++] = mit[x] == ' ' ? kar : mit[x];

    for (int x = 0; x < db; ++x)
        ide[pos++] = kar;

    ide[pos] = '\0';
}

int main(void) {
    char novelt[30];

    strelemoge(novelt, "Hello hallo elektor kalandor", 'x', 3);
    printf("[%s]\n", novelt);

    return 0;
}

Második szó, utolsó szó

Kis ZH volt

Írj függvényt, amely paraméterként kap egy sztringet! A sztring szöveget tartalmaz, melynek szavait szóközök választják el egymástól (minden szó, ami nem szóköz). A függvény adja vissza címével átvett változókban a sztring második szavának indexét, visszatérési értékként (return-nel) pedig a sztring utolsó szavának címét! A paraméterként kapott sztringről biztosan tudjuk, hogy legalább két szóból áll, a szavakat pontosan egy szóköz választja el egymástól, és a sztring első és utolsó karaktere nem szóköz.

Megoldás
char *szavak(char *sztring, int *masodik) {
    int utolso = 0;
    *masodik = 0;
    for (int i = 0; sztring[i] != '\0'; i++) {
        if (sztring[i] == ' ') {
            utolso = i;
            if (*masodik == 0)
                *masodik = i + 1;
        }
    }
    return sztring + utolso + 1;
}

„The” kezdetű címek"

Könyvek, filmet címeit úgy szokás rendezni, hogy a címek elején lévő névelőket (pl. angolul a „The”, magyarul az „A” és „Az”) a rendezésben nem vesszük figyelembe. Írj egy olyan módosított strcmp_the() függvényt, amely paramétere és visszatérési értéke az eredeti strcmp()-éhez hasonló, de az összehasonlításnál figyelmen kívül hagyja a „The” kezdetet!

Megoldás

A legegyszerűbb megoldás az alábbi. Megvizsgáljuk mindkét sztringet. Ha valamelyik a „The ” részsztringgel kezdődik, az annak megfelelő pointert 4-gyel növeljük, tehát négy karakterrel hátrébb léptetjük. Az így kapott sztringeket adjuk az eredeti strcmp()-nek. A két pointer növelését azért tehetjük meg, mert azok a saját függvényünknek lokális változói, amelyet módosíthatunk.

/* Sztring összehasonlító függvény, ami nem veszi figyelembe
 * a szting elején lévő "The " előtagot */
int strcmp_the(char *egyik, char *masik) {
    if (strncmp(egyik, "The ", 4) == 0)
        egyik += 4;
    if (strncmp(masik, "The ", 4) == 0)
        masik += 4;
    return strcmp(egyik, masik);
}

IP cím

Vizsga volt

Írj egy olyan szabványos ANSI C függvényt, amely paraméterként kap egy sztringet, mely egy IP címet tartalmaz a szokásos alakban: négy darab 0 és 255 közötti szám pontokkal elválasztva. A függvény állítsa elő az IP cím 32 bites reprezentációját! A visszatérési értéke legyen egy unsigned érték, amelynek legalsó bájtja az IP cím utolsó részének megfelelő értéket tartalmazza, a második az IP cím utolsó előtti részét és így tovább. Feltesszük, hogy az unsigned típus az adott architektúrán legalább 32 bites. Ha a bemenet például "0.0.2.33", akkor a kimenet: 545.

Megoldás

Barátunk a scanf(). Itt egyáltalán nem kell karakterenkénti feldolgozást, és semmi hasonlót csinálni. A scanf %u be fog olvasni egy nemnegatív számot, és a pontnál meg fog állni, mivel a pont nem lehet része egy egész számnak. A formátumsztringbe pedig ha elhelyezünk egy pontot, akkor a scanf azt fogja várni, hogy a bemeneten ott is legyen az a pont; beolvassa és eldobja. És hát, mivel nem a standard bemenetről olvasunk, hanem sztringből, sscanf() lesz a függvényünk. Vigyázni kell, hogy a << műveleteket zárójelezni kell az összeadásnál – itt inkább bitenkénti vagy kapcsolatot használtam (az eredmény egyébként ugyanaz lenne).

#include <stdio.h>

unsigned str_to_ip(char *str) {
    unsigned a, b, c, d;

    sscanf(str, "%u.%u.%u.%u", &a, &b, &c, &d);
    return a<<24 | b<<16 | c<<8 | d;
}

int main(void) {
    printf("%u\n", str_to_ip("0.0.2.33"));
    return 0;
}