10. gyakorlat

A gyakorlat anyaga

A gyakorlat elején 45 percben kerül megírásra a 7. zh. A feladatok témái:

  • algoritmus elkészítése
  • tömbökkel megoldható feladatok
  • önálló függvények megírása (bemenet/kimenet kezelés nélkül)
  • szemantikus hibajavítás: szintaktikailag helyes, de rosszul működő függvény(ek) kijavítása

Haladó IO

A printf és scanf függvényeket (illetve ezek fájlkezelő verzióit) eddig csak éppen használgattuk, egyszerű értékek beolvasására illetve kiírására. A kiíratásnál már láthattuk, hogy a printf azért elég "okos", sok mindent tud. Érdemes viszont ezeket a függvényeket alaposabban ismerni, mert van pár probléma, aminek a megoldásában sokat segíthetnek.

Feladat (f0161)

Feladat:

Készíts egy programot, ami tízes számrendszerbeli alakban beolvas egy előjeltelen egész értéket, és nyolcas számrendszerbe átváltva kiírja azt. Minél nagyobb számokat tud kezelni, annál jobb.

Segítség

Gondolkozz egy pár percet, mielőtt megnéznéd az alábbi ötleteket, mert lehet, hogy magadtól is rájössz egy megoldásra. Segítségképpen egy jótanács: az egész típus C-ben elemi adattípusnak számít, az értéke nem felbontható! Ez azt jelenti, hogy a tárolt érték nem egy 10-es vagy 2-es vagy akárhanyas számrendszerbeli szám, hanem "csak" egy szám (ami éppen a számítógépek fizikai adottságai miatt kettes számrendszerben van tárolva). A szám 10-es vagy 8-as számrendszerbeli alakja pedig egy számleírás, egy karaktersorozat, egy sztring. Vagyis, beolvasáskor egy 10-es számrendszerbeli számleírást (karaktersorozatot) kell számmá alakítani, kiíráskor pedig egy számot kell 8-as számrendszerbeli számleírássá (karaktersorozattá) alakítani.

1. ötlet

Használjuk ki, hogy ha egy n számot valamilyen a számrendszerben szeretnénk leírni, akkor a leírás utolsó számjegyét megkapjuk az n % a művelettel, az előző számjegyeket (már ha vannak) pedig úgy, hogy az n / a számot leírjuk az a számrendszerben. Vagyis a számjegyeket fordított sorrendben kapjuk meg, így előbb mondjuk egy karaktertömbben kell eltárolni őket, és ha mind megvan, akkor lehet kiíratni (megfelelő sorrendben). A karaktertömb mérete legfeljebb akkora, ahány számjegy a legnagyobb adott típuson ábrázolható számhoz szükséges. Ez az algoritmus bármilyen a számrendszer esetén működik.

Lehetséges megoldás (m0161-1.c)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 *
 * Programozás Alapjai
 *
 * Keleti Márton, 2018. őszi félév.
 *
 * Specifikáció:
 *    Input: egy nem negatív egész szám 10-es számrendszerben
 *    Output: az N egész szám 8-as számrendszerben
 *
 * Algoritmustervezés:
 *    A 10-es számrendszerbeli számot egyszerűen scanf()-fel beolvassuk egy
 *    lehető legnagyobb előjel nélküli egész típusú változóba. Létrehozunk
 *    egy eredmény karaktertömböt. Az átváltást egy ciklus végzi, ami addig
 *    számol 8-as osztási maradékot és hányadost, amíg el nem éri a 0-t.
 *    A maradék lesz az adott helyiértéknek megfelelő számérték, ezt a tömbbe
 *    már karakterként tesszük be, azaz hozzáadjuk a '0' karakter kódját. A
 *    tömbben így a ciklus végére a 8-as számrendszerbeli alak lesz a ciklus
 *    végén, viszont fordítva. Ezért kell még egy ciklus, ami a tömböt a
 *    végétől kezdve karakterenként kiírja.
 *
 * Fordítás:
 *    gcc -Wall -o m0161-1 m0161-1.c
 *
 * Futtatás:
 *    ./m0161-1
 */

#include <stdio.h>
#include <stdlib.h>

int main() {
    // beolvassuk a 10-es számrendszerbeli számot
    unsigned long long int n;
    scanf("%llu", &n);  // %llu: long long unsigned

    /*
     * A tömb méretét úgy kapjuk meg, hogy a sizeof() operátorral megtudjuk az
     * eredeti típusunk méretét bájtban. Ezt 8-cal szorozva megkapjuk hány bit,
     * amit 3-mal osztva a szükséges 8-as számrendszerbeli karakterek számát
     * kapjuk, hiszen egy ilyen karakter 0-7-ig tárolhat számokat, ami pont 3
     * biten fér el. Hozzá kell még adnunk egyet az eredményhez, ugyanis a 3
     * valószínűleg nincs meg maradék nélkül a bitek számában.
     * (pl 64 / 3 = 21+1/3)
     */
    int bitek = sizeof(unsigned long long int) * 8;
    int nyolcasok = bitek / 3 + 1;

    // dinamikusan foglalunk nyolcasok darab karakternek helyet
    char* tomb = (char*)malloc(nyolcasok * sizeof(char));
    int i = 0;

    do {
        char mod = n % 8;
        n /= 8;
        tomb[i++] = mod + '0'; // itt nem mindegy, hogy prefix vagy postfix ++
    } while (n > 0);

    // i az utolsó utáni elemre mutat, ezért először csökkentjük eggyel
    --i;
    while (i >= 0) {
        printf("%c", tomb[i--]);
    }
    printf("\n");

    free(tomb);

    return 0;
}
2. ötlet

Gondoljuk végig, mit tud a scanf() és printf(). Milyen számokat tud beolvasni illetve milyen formátumokban tud kiírni? Lehet, hogy a 10-esből 8-asba váltásra van egy, az előzőnél (legalábbis C programozói szinten) egyszerűbb megoldás is?

Lehetséges megoldás (m0161-2.c)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 *
 * Programozás Alapjai
 *
 * Keleti Márton, 2018. őszi félév.
 *
 * Specifikáció:
 *    Input: egy nem negatív egész szám 10-es számrendszerben
 *    Output: az N egész szám 8-as számrendszerben
 *
 * Algoritmustervezés:
 *    Kihasználjuk a printf() függvény %o formázókarakterét, amely 8-as
 *    számrendszerben jelenít meg egy egész számot.
 *
 * Fordítás:
 *    gcc -Wall -o m0161-2 m0161-2.c
 *
 * Futtatás:
 *    ./m0161-2
 */

#include <stdio.h>

int main() {
    unsigned long long int n;
    scanf("%llu", &n);
    printf("%llo\n", n);
    return 0;
}
Lehetséges megoldás - (videó)

m0161

Feladat (f0174)

Feladat:

Egy sor kiíratási formátuma: "nev: %s; pont: %d;".

  1. Olvasd be a kiírt számot úgy, ha tudod, hogy a kiírt sztring nem tartalmazhat pontosvesszőt, de bármi mást igen. Ellenőrizd le, hogy az input sor valóban helyes-e?
  2. Módosítsd úgy a programot, hogy az stdin és stdout fájlokat, valamint a fscanf és fprintf függvényeket használja.
  3. Módosítsd úgy a programot, hogy valódi fájlokat használjon. Hibaüzenettel és hibakóddal lépjen ki a program, ha valamelyik fájl megnyitása nem sikerült.
Segítség

Figyelj a feladatra: csak a sorszámot kell beolvasni, de előtte még van egy csomó karakter.

Hogyan működik a scanf? Ha a formátumsztringben egy fix karakter van (azaz nem valami %-kal kezdődő konverziós specifikáció), akkor azt megpróbálja beolvasni, és ha nem sikerül, nem megy tovább. Vagyis a printf-ben használt formátumsztring jó kiindulási alap, hiszen a fix karakterei egyeznek a beolvasandó soréval. A %s-sel ugyanakkor bármilyen sztringértéket ki tudunk írni, míg beolvasáskor egy egybefüggő, whitespace karaktereket nem tartalmazó karaktersorozatot fog beolvasni. Vagyis pl. a "Hello Vilag!"-ból csak a "Hello"-t, ugyanakkor a "pontos;vesszo"-t teljesen beolvasná. A feladat szerint itt egy ';'-t nem, de bármi mást esetleg tartalmazó karaktersorozat áll. Nézd meg, előadáson hogyan oldottuk meg mondjuk egy szám beolvasása után sor maradék részének "lenyelését".

És honnan fogjuk tudni, hogy sikerült-e beolvasni a számot? A scanf visszatérési értéke fogja megmondani, hogy hány értéket sikerült beolvasni. Jelen esetben ennek 1-nek kell lennie.

Lehetséges megoldás (m0174-1.c)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 *
 * Programozás Alapjai feladat megoldása
 *
 * Gergely Tamás, 2008. őszi félév.
 *
 * fordítás:
 *    gcc -o m0174-1 m0174-1.c
 *
 * futtatás:
 *    ./m0174-1
 */

#include <stdio.h>

int main() {
    int val, ret;
    ret = scanf("nev: %*[^;]; pont: %d;", &val);
    if (ret == 1) {
        printf("A szám: %d\n", val);
    } else {
        printf("Helytelen formátum\n");
    }
    return 0;
}
Segítség

Ez egyszerű: a scanf(...)-ből fscanf(stdin, ...), a printf(...)-ből printf(stdout, ...) lesz.

Lehetséges megoldás (m0174-2.c)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 *
 * Programozás Alapjai feladat megoldása
 *
 * Gergely Tamás, 2008. őszi félév.
 *
 * fordítás:
 *    gcc -o m0174-2 m0174-2.c
 *
 * futtatás:
 *    ./m0174-2
 */

#include <stdio.h>

int main() {
    int val, ret;
    ret = fscanf(stdin, "nev: %*[^;]; pont: %d;", &val);
    if (ret == 1) {
        fprintf(stdout, "A szám: %d\n", val);
    } else {
        fprintf(stdout, "Helytelen formátum\n");
    }
    return 0;
}
Segítség

Ilyet már csináltál: kell hozzá két FILE* típusú változó az stdin és stdout helyett, a fájlokat használat előtt meg kell nyitni az fopen() függvény segítségével, használat után pedig le kell zárni az fclose()-zal. Ha az fopen() egy NULL értékkel tér vissza, az bajt jelent.

Lehetséges megoldás (m0174-3.c)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 *
 * Programozás Alapjai feladat megoldása
 *
 * Gergely Tamás, 2008. őszi félév.
 *
 * fordítás:
 *    gcc -o m0174-3 m0174-3.c
 *
 * futtatás:
 *    ./m0174-3
 */

#include <stdio.h>

int main() {
    int val, ret;
    FILE *infile;
    FILE *outfile;
    if (!(infile = fopen("be.txt", "r"))) {
        fprintf(stderr, "Hiba: a be.txt nem olvasható\n");
        return 1;
    }
    if (!(outfile = fopen("ki.txt", "w"))) {
        fprintf(stderr, "Hiba: a ki.txt nem írható\n");
        fclose(infile);
        return 1;
    }
    ret = fscanf(infile, "nev: %*[^;]; pont: %d;", &val);
    if (ret == 1) {
        fprintf(outfile, "A szám: %d\n", val);
    } else {
        fprintf(outfile, "Helytelen formátum\n");
    }
    fclose(infile);
    fclose(outfile);
    return 0;
}

Pointer haladó

Feladat (f0186)

Feladat:

Fordítsd le és futtasd a linkedlist.c programot. Ez egy láncolt listába olvas be egész számokat egy adott végjelig. Módosítsd a programot, hogy kiíratáskor kiírja a cella címét, a cella két mezőjének címét és azok értékét is. Hasonlítsd össze a cellák címeit a kov mezők értékeivel.

linkedlist.c:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 *
 * Programozás Alapjai
 */

#include <stdio.h>
#include <stdlib.h>

#define VEGJEL 0

struct cella {
    int ertek;
    struct cella *kov;
};

int main() {
    struct cella *elso = NULL;
    struct cella *utolso = NULL;
    struct cella *p;
    int i;
    scanf("%d", &i);
    while (i != VEGJEL) {
        p        = (struct cella*)malloc(sizeof(struct cella));
        p->ertek = i;
        p->kov   = NULL;
        if (elso == NULL) {
            elso = p;
        } else {
            utolso->kov = p;
        }
        utolso   = p;
        scanf("%d", &i);
    }
    for (p = elso; p != NULL; p = p->kov) {
        printf("%d\n", p->ertek);
    }
    while (elso != NULL) {
        p    = elso;
        elso = p->kov;
        free(p);
    }
    return 0;
}

Lehetséges megoldás (m0186.c)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 *
 * Programozás Alapjai
 */

#include <stdio.h>
#include <stdlib.h>

#define VEGJEL 0

struct cella {
    int ertek;
    struct cella *kov;
};

int main() {
    struct cella *elso = NULL;
    struct cella *utolso = NULL;
    struct cella *p;
    int i;
    scanf("%d", &i);
    while (i != VEGJEL) {
        p        = (struct cella*)malloc(sizeof(struct cella));
        p->ertek = i;
        p->kov   = NULL;
        if (elso == NULL) {
            elso = p;
        } else {
            utolso->kov = p;
        }
        utolso   = p;
        scanf("%d", &i);
    }
    printf("%14s %14s %14s %14s %8s\n", "p", "&p->ertek", "&p->kov", "p->kov", "p->ertek");
    for (p = elso; p != NULL; p = p->kov) {
        printf("%14p %14p %14p %14p %8d\n", p, &p->ertek, &p->kov, p->kov, p->ertek);
    }
    while (elso != NULL) {
        p    = elso;
        elso = p->kov;
        free(p);
    }
    return 0;
}
Válaszok (f0186)

Egy-egy cella címe megegyezik az előző cella kov mezőjének értékével. Az ertek mezők címei megegyeznek az őket tartalmazó cella címeivel, míg a kov mezők címei ennél mindig ugyanannyival (sizeof(int)-tel, ami manapság igen nagy valószínűséggel 4 lesz) nagyobbak.

Feladat (f0261)

Feladat:

Írj egy programot, ami egy láncolt listába olvas be egész számokat egy konstansként megadott végjelig, majd fordított sorrendben kiírja a beolvasott értékeket.

Ötlet

Érdemes az előző feladatból kiindulni. Ha a listába eleve fordítva tesszük bele az elemeket, akkor a kiíratás mehet ugyanúgy előre, mint abban. A trükk: az új elem mindig a lista kezdőeleme lesz, és e mögé fűzzük oda az eddigi listát.

Lehetséges megoldás (m0261.c)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 *
 * Programozás Alapjai
 *
 * Specifikáció:
 *    Input: egész számok sorozata egy konstans végjellel lezárva
 *    Output: az input számok fordított sorrendben
 *
 * Algoritmustervezés:
 *    A láncolt listát úgy valósítjuk meg, hogy mindig az elejére fűzzük fel az
 *    új elemeket, így egy LIFO (Last In First Out) adatszerkezetet kapunk.
 *    Mivel a legutolsóként beolvasott szám van a lánc legelején, egyszerűen
 *    végigjárva a láncelemeket, fordított sorrendben fogjuk megkapni az
 *    értékeket.
 *
 * Megjegyzés:
 *    Láncolt listával FIFO (First In First Out) működés is könnyen
 *    megvalósítható: ehhez a lánc elejére mutató pointeren kívül egy a végére
 *    mutatóra is szükség van a feltöltéskor, és mindig az utolsó elem után
 *    kell fűzni az újakat, majd a pointert átállítanil.
 *
 * Fordítás:
 *    gcc -Wall -o m0261 m0261.c
 *
 * Futtatás:
 *    ./m0261
 */

#include <stdio.h>
#include <stdlib.h>

#define VEGJEL 0

struct cella {
    int ertek;
    struct cella *kov;
};

int main() {
    struct cella *elso = NULL;
    struct cella *p;
    int i;
    scanf("%d", &i);
    while (i != VEGJEL) {
        p        = (struct cella*)malloc(sizeof(struct cella));
        p->ertek = i;
        p->kov   = elso;
        elso     = p;
        scanf("%d", &i);
    }
    for (p = elso; p != NULL; p = p->kov) {
        printf("%d ", p->ertek);
    }
    printf("\n");
    while (elso != NULL) {
        p    = elso;
        elso = p->kov;
        free(p);
    }
    return 0;
}

Függvény pointerek

A pointerek mutathatnak nem csak adatra, de algoritmusra, C esetében függvényekre is. Ez azt jelenti, hogy futásidőben tudjuk kiválasztani, hogy (azonos visszatérési típusú és paramétereket váró) függvények közül melyik hívódjon egy adott ponton. A függvény pointer típusú változó deklarációja olyan, mint a függvény deklaráció, csak a szimpla függvénynév helyére egy pointerdeklarációt rakunk (amit a precedencia miatt külön zárójelezünk):

int (*fgv_ptr)(int, int);

Ha mindez elé kiírjuk a typedef kulcsszót is, akkor a megadott név a függvény pointer típust fogja azonosítani:

typedef int (*fgv_t)(int, int);

Egy-egy függvény neve önmagában a függvényre mutató pointert jelenti. (Ezért van, hogy ha meg akarjuk hívni, akkor üres paraméterlista esetén is ki kell tenni a () zárójelpárt, különben csak egy függvény pointer értéket eredményező kifejezést kapunk.) Vagyis, ha egy függvény pointernek értéket szeretnénk adni, egyszerűen értékül adjuk neki a függvényt anélkül, hogy a () használatával meghívnánk azt. Ha pl.

1
2
3
int osszead(int a, int b) {
    return a + b;
}
a függvénydefiníciónk, akkor a

fgv_ptr = osszead;

értékadás a fgv_ptr számára. (A fgv_ptr = osszead(...); viszont értelmetlen, mert meghívná a függvényt, ami nep pointert, hanem int-et ad vissza.) Ezek után ha meghívjuk a függvényt a fgv_ptr-en keresztül, akkor valójában az osszeg-et hívjuk:

x = fgv_ptr(3, 5);

A függvénypointerek megértését segítheti a következő videó, mely a függvény pointerek alapjait mutatja be:

ytv_func_ptr

Feladat (f0282)

Problémafelvetés:

Készíts egy programot, amely egy tetszőleges nagyságú (maximum 9999 jegyű) számról a jól ismert oszthatósági szabályok felhasználásával eldönti, hogy a szám osztható-e egy [2..12] intervallumba eső számmal.

Specifikáció:

A program inputja egy 2 és 12 közötti egész szám, majd szóközzel elválasztva egy legfeljebb 9999 jegyű egész szám. A program outputja egy egyszerű "Igen" vagy "Nem" attól függően, hogy a második szám osztható-e az elsővel.

Algoritmustervezés/Megvalósítás:

A programban a megfelelő oszthatóságot ellenőrző függvényt egy függvény pointeren keresztül hívjuk meg.

Segítség

Érdemes a 6. gyakorlat f0166-os feladatának megoldásából kiindulni.

Lehetséges megoldás (m0282.c)
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 *
 * Programozás Alapjai
 *
 * Gergely Tamás, 2020. őszi félév.
 *
 * fordítás:
 *    gcc -o m0282 m0282.c
 *
 * futtatás:
 *    ./m02828
 */

#include <stdio.h>

int o2(char* sz) {
    int i, s = 0;
    for (i = 0; sz[i] != 0; ++i);
    if (i > 0) {
        s += (sz[--i] - '0');
    }
    return s % 2 == 0;
}

int o3(char* sz) {
    int i, s = 0;
    for (i = 0; sz[i] != 0; ++i) {
        s += sz[i] - '0';
    }
    return s % 3 == 0;
}

int o4(char* sz) {
    int i, s = 0;
    for (i = 0; sz[i] != 0; ++i);
    if (i > 0) {
        s += (sz[--i] - '0');
        if (i > 0) {
            s += 10 * (sz[--i] - '0');
        }
    }
    return s % 4 == 0;
}

int o5(char* sz) {
    int i, s = 0;
    for (i = 0; sz[i] != 0; ++i);
    if (i > 0) {
        s += (sz[--i] - '0');
    }
    return s % 5 == 0;
}

int o6(char* sz) {
    return o2(sz) && o3(sz);
}

int o7(char* sz) {
    int i, s = 0;
    for (i = 0; sz[i] != 0; ++i);
    while (i > 0) {
        int t = 0;
        if (i > 0) {
            t += (sz[--i] - '0');
            if (i > 0) {
                t += 10 * (sz[--i] - '0');
                if (i > 0) {
                    t += 100 * (sz[--i] - '0');
                }
            }
        }
        s = t - s;
    }
    return s % 7 == 0;
}

int o8(char* sz) {
    int i, s = 0;
    for (i = 0; sz[i] != 0; ++i);
    if (i > 0) {
        s += (sz[--i] - '0');
        if (i > 0) {
            s += 10 * (sz[--i] - '0');
            if (i > 0) {
                s += 100 * (sz[--i] - '0');
            }
        }
    }
    return s % 8 == 0;
}

int o9(char* sz) {
    int i, s = 0;
    for (i = 0; sz[i] != 0; ++i) {
        s += sz[i] - '0';
    }
    return s % 9 == 0;
}

int o10(char* sz) {
    return o5(sz) && o2(sz);
}

int o11(char* sz) {
    int i, s = 0;
    for (i = 0; sz[i] != 0; ++i) {
        s = sz[i] - '0' - s;
    }
    return s % 11 == 0;
}

int o12(char* sz) {
    return o4(sz) && o3(sz);
}

int main() {
    int (*fgv_ptr)(char*) = NULL;
    int oszto;
    char szam[10000];
    scanf("%d %9999s", &oszto, szam);
    switch (oszto) {
        case  2: fgv_ptr =  o2; break;
        case  3: fgv_ptr =  o3; break;
        case  4: fgv_ptr =  o4; break;
        case  5: fgv_ptr =  o5; break;
        case  6: fgv_ptr =  o6; break;
        case  7: fgv_ptr =  o7; break;
        case  8: fgv_ptr =  o8; break;
        case  9: fgv_ptr =  o9; break;
        case 10: fgv_ptr = o10; break;
        case 11: fgv_ptr = o11; break;
        case 12: fgv_ptr = o12; break;
    }
    printf("A(z) %d%s osztója a számnak.\n", oszto, fgv_ptr(szam) ? "" : " nem");
    return 0;
}

A függvény pointerek mélyebb megértést segítheti a függvény pointerek használata a beszúró rendezéshez: ytv_ins_sort_func_ptr

A következő videó pedig a függvény pointereket használja egy számológép megvalósításához: ytv_calc_func_ptr

Látványos feladatok

Feladat (f0231)

Problémafelvetés:

Invertálj egy PGM képet, azaz minden képpont intenzitását változtasd az ellenkezőjére (így pl. feketéből fehér, világosból sötét lesz, mint a fotónegatívokon)!

Specifikáció:

A program inputja egy (egyszerűsített) PGM formátumú kép. A PGM formátum egy szürkeárnyalatos képet ír le a következő formában. A PGM fájl első sora a P2 szöveg; a második sortól kezdve egész értékeket tartalmaz whitespace karakterekkel (szóköz, tabulátor, sortörés, stb.) elválasztva. Az első két érték X és Y, a kép szélessége és magassága, a harmadik M, a képpontok lehetséges maximális értéke. Ezt további X*Y darab [0..M] intervallumba eső érték követi, az egyes képpontok intenzitásértékei, sorfolytonosan megadva. A kimenet egy PGM formátumú kép, az eredeti kép inverze.

Példa input fájlok: torony-o.pgm, progalap.pgm.

1. ötlet

Van egy célorientált, egyszerű megoldás. Gondoljuk végig, mit is kell csinálni? Az input elején van egy "P2\n" sor, ez fix beolvasásnál és kiírásnál is. Ezután van valamennyi egész szám: legalább 3 darab, és az első kettőből kiszámolható, hogy még mennyi. És a kimenet mi lesz? Ugyanúgy a "P2\n" sor, utána pedig ugyanannyi szám, mint amennyit beolvastunk. Az első 3 szám változik? Nem, amint beolvastuk, akár ki is írhatjuk őket. Az utánuk következő számok változnak? Igen, de amint megvan a szám, egyből ki tudjuk belőle (és a 3. számból) számolni, hogy mit kellene kiírni, és ki is írhatjuk.

Lehetséges megoldás (m0231-1.c)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 *
 * Programozás Alapjai feladat megoldása
 *
 * Gergely Tamás, 2018. őszi félév.
 *
 * Algoritmustervezés:
 *    Először egy célorientált megvalósítást készítünk, ami jól működik, de
 *    nem kifejezetten szép. Ennek lényege, hogy a kimenetben az alapadatok
 *    megegyeznek a bemeneti kép adataival, a pontok intenzitása viszont a
 *    maximális intenzitásból kivont régi intenzitás lesz. Ehhez valójában
 *    nem szükséges tárolni a pontokat, az invertálást "on the fly" el lehet
 *    végezni.
 *
 * fordítás:
 *    gcc -o m0231-1 m0231-1.c
 *
 * futtatás:
 *    ./m0231-1
 */

#include <stdio.h>

int main() {
    int w, h, m;
    scanf("P2\n%d %d %d", &w, &h, &m);
    printf("P2\n%d %d\n%d\n", w, h, m);
    for (int i = w * h; i > 0; --i) {
        int v;
        scanf("%d", &v);
        printf(" %d", m - v);
    }
    printf("\n");
    return 0;
}
2. ötlet

Van egy "szép" megoldás is: csinálunk egy adattípust (struktúrát) a kép tárolására. A beolvasás egy ilyen képet tölt fel adatokkal, a kiírás pedig egy ilyen kép adatait írja ki. A tárolt adatokat pedig a kettő között kedvünkre manipulálhatjuk. A kép adattípusban a kép méreteit, a maximális intenzitást, és a pontok értékét kell tárolni. A pontok értékének tárolására egy dinamikus tömböt érdemes használni (pointer adattípusú mezőn keresztül).

Lehetséges megoldás (m0231-2.c)
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 *
 * Programozás Alapjai feladat megoldása
 *
 * Gergely Tamás, 2017. őszi félév.
 *
 * fordítás:
 *    gcc -o m0231-2 m0231-2.c
 *
 * futtatás:
 *    ./m0231-2
 */

#include <stdio.h>
#include <stdlib.h>

/*
 * A képnek készítsünk egy adattípust. Egy szürkeárnyalatos képet kell
 * tárolnunk, melynek van szélessége (w), magassága (h), maximális képpont-
 * intenzitása (m), illetve mind a w*h pontjához tartozik egy-egy képpont-
 * intenzitási érték. Erre egy olyan struktúra felelne meg, amely három
 * egész értéket, és egy kétdimenziós, w*h méretű tömböt tartalmaz. Egy
 * struktúra tömbjét viszont csak konstans mérettel lehet deklarálni, így a
 * tömb helyett pointert használunk, amin keresztül majd akkor foglalunk
 * helyet a képpontoknak, amikor már tudjuk, hogy mennyi van. Továbbá, az
 * egyszerűbb helyfoglalás érdekében a helyet egyben foglaljuk le
 * (egydimenziós tömbként), így egy egyszerű int-re mutató pointert kapunk
 * (és nem int pointerre mutató pointert, mint valódi kétdimenziós foglalás
 * esetén). Vegyük észre, hogy a "P2" sztring a formátumhoz tartozik, így
 * nem kell tárolnunk. Egyrészt mindig ugyanaz, tehát felesleges, másrészt
 * az adatstruktúránk bármilyen szürkeárnyalatos képet képes tárolni, akkor
 * is, ha az pl. png formátumban van elmentve, mert az adattartalma akkor is
 * egy kép. (Az más kérdés, hogy a beolvasás kicsit bonyolultabb lenne.)
 */

typedef struct {
    int w;     // a kép szélessége
    int h;     // a kép magassága
    int m;     // a maximális képpont-intenzitás
    int *data; // a képpontok intenzitás-értékei
} kep_t;

/* Haladjunk modulárisan. Szükség lesz egy PGM formátumú kép betöltésére.
 * Erre készítsünk egy függvényt, ami a standard inputról olvas, és egy
 * képpel (kep_t) tér vissza. Nem csinálunk hibaellenőrzést, feltételezzük,
 * hogy egy (kommentek nélküli) PGM képet kapunk.
 */

kep_t beolvas() {
    int n, i;
    kep_t retval = {0, 0, 0, NULL};
    scanf("P2\n");             // Beolvassuk az első sort, ez fix tartalmú.
    scanf("%d %d", &retval.w, &retval.h); // A kép szélessége, magassága.
    scanf("%d", &retval.m);               // A maximális képpont-intenzitás.
    /* Most, hogy ismerjük a kép méreteit, már tudunk helyet foglalni a
     * képpontok számára.
     */
    n = retval.w * retval.h;        // Képpontok száma, egy segédváltozóban.
    retval.data = malloc(n * sizeof(int));
    // Ezután már be tudjuk olvasni a képpontokat, sorfolytonosan.
    for (i = 0; i < n ; ++i) {
        scanf("%d", &retval.data[i]);
    }
    /* Megjegyeznénk, hogy a whitespace karakterekkel -- mint például a
     * sortörések -- nem kellett külön foglalkozni, mert a %d specifikáció
     * beolvasásnál automatikusan továbblép ezeken.
     */
    return retval;
}

/* Mivel a kép létrehozásakor dinamikusan foglaltunk memóriát, szükség lesz
 * ennek a felszabadítására. Erre is csináljunk egy függvényt.
 * Megjegyeznénk, hogy a függvény a kép adatain (a képpontok törlésén kívül)
 * nem változtat, vagyis azt, hogy egy kép törölve volt-e, futás közben
 * utólag így már lehetetlen megállapítani. Tehát a mi (programozói)
 * felelősségünk, hogy egy törölt képen már ne végezzünk semmilyen
 * műveletet.
 */

void torol(kep_t kep) {
    free(kep.data);
}

/* Beolvasás (és egyben létrehozás) valamint törlés megvolt, jöhet a kiírás,
 * szintén külön függvényben. Ez a beolvasás "tükörképe" lesz.
 */

void kiir(kep_t kep) {
    int n, i;
    printf("P2\n");                     // Kiírjuk a formátum első sortát,
    printf("%d %d\n", kep.w, kep.h);    // a kép szélességét, és magasságát,
    printf("%d", kep.m);                // a maximális képpont-intenzitást.
    n = kep.w * kep.h;                  // Képpontok száma.
    for (i = 0; i < n ; ++i) {
        /* A lenti kiírásban a trükk: a kiírt számokat szóközökkel
         * választjuk el, de minden képsor elején (amikor az index osztható
         * a kép szélességével) a szóköz helyett egy sorvége karaktert írunk
         * ki, így minden képsor adatai a kiírásban is új sorban fognak
         * kezdődni.
         */
        printf("%c%d", (i % kep.w) ? ' ' : '\n', kep.data[i]);
    }
    printf("\n");                       // Az utolsó sor lezárása.
    /* Megjegyeznénk, hogy a whitespace karakterek helyzete, vagyis hogy az
     * output hány sor (a kötelező elsőn túl), a saját döntésünk.
     */
}

/* Már csak az invertálás maradt hátra. Ez nem túl nehéz feladat, minden
 * képpontra függetlenül végrehajtható, és csak a képpont eredeti értékétől
 * (meg persze a maximális képpont-intenzitástól függ). A kapott képen
 * fogunk dolgozni. Mivel a kép direkt adatait nem változtatjuk (csak a
 * pointer által mutatott adatokat), a képet elegendő érték szerint átvenni.
 */

void invertal(kep_t kep) {
    int n, i;
    n = kep.w * kep.h;                  // Képpontok száma.
    for (i = 0; i < n ; ++i) {
        kep.data[i] = kep.m - kep.data[i];
    }
}

/* Már csak a főprogram van hátra. */

int main() {
    kep_t kep;
    kep = beolvas();
    invertal(kep);
    kiir(kep);
    torol(kep);
    return 0;
}

Feladat (f0234)

Problémafelvetés:

Invertálj egy PGM képet, azaz minden képpont intenzitását változtasd az ellenkezőjére (így pl. feketéből fehér, világosból sötét lesz, mint a fotónegatívokon)!

Specifikáció:

A program inputja egy (egyszerűsített) PGM formátumú kép. A PGM formátum egy szürkeárnyalatos képet ír le a következő formában. A PGM fájl első sora a P2 szöveg; a második sortól kezdve egész értékeket tartalmaz whitespace karakterekkel (szóköz, tabulátor, sortörés, stb.) elválasztva. Az első két érték X és Y, a kép szélessége és magassága, a harmadik M, a képpontok lehetséges maximális értéke. Ezt további X*Y darab [0..M] intervallumba eső érték követi, az egyes képpontok intenzitásértékei, sorfolytonosan megadva. A kimenet egy PGM formátumú kép, az eredeti kép inverze.

A képet az input.pgm fájlból kell beolvasni és az output.pgm fájlba kell kiírni.

Példa input fájlok: torony-o.pgm, progalap.pgm.

Ötlet

Érdemes az előző feladat megoldását bővíteni.

Lehetséges megoldás (m0234-1.c)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 *
 * Programozás Alapjai feladat megoldása
 *
 * Gergely Tamás, 2018. őszi félév.
 *
 * Algoritmustervezés:
 *    Először egy célorientált megvalósítást készítünk, ami jól működik, de
 *    nem kifejezetten szép. Ennek lényege, hogy a kimenetben az alapadatok
 *    megegyeznek a bemeneti kép adataival, a pontok intenzitása viszont a
 *    maximális intenzitásból kivont régi intenzitás lesz. Ehhez valójában
 *    nem szükséges tárolni a pontokat, az invertálást "on the fly" el lehet
 *    végezni.
 *
 * fordítás:
 *    gcc -o m0234-1 m0234-1.c
 *
 * futtatás:
 *    ./m0234-1
 */

#include <stdio.h>
#include <stdlib.h>

int main() {
    int w, h, m;
    FILE *be, *ki;
    be = fopen("input.pgm", "r");
    if (be == NULL) {
        return 1;
    }
    ki = fopen("output.pgm", "w");
    if (ki == NULL) {
        fclose(be);
        return 1;
    }
    fscanf(be, "P2\n%d %d %d", &w, &h, &m);
    fprintf(ki, "P2\n%d %d\n%d\n", w, h, m);
    for (int i = w * h; i > 0; --i) {
        int v;
        fscanf(be, "%d", &v);
        fprintf(ki, " %d", m - v);
    }
    fprintf(ki, "\n");
    fclose(be);
    fclose(ki);
    return 0;
}
Lehetséges megoldás (m0234-2.c)
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 *
 * Programozás Alapjai feladat megoldása
 *
 * Gergely Tamás, 2017. őszi félév.
 *
 * fordítás:
 *    gcc -o m0231-2 m0231-2.c
 *
 * futtatás:
 *    ./m0231-2
 */

#include <stdio.h>
#include <stdlib.h>

/*
 * A képnek készítsünk egy adattípust. Egy szürkeárnyalatos képet kell
 * tárolnunk, melynek van szélessége (w), magassága (h), maximális képpont-
 * intenzitása (m), illetve mind a w*h pontjához tartozik egy-egy képpont-
 * intenzitási érték. Erre egy olyan struktúra felelne meg, amely három
 * egész értéket, és egy kétdimenziós, w*h méretű tömböt tartalmaz. Egy
 * struktúra tömbjét viszont csak konstans mérettel lehet deklarálni, így a
 * tömb helyett pointert használunk, amin keresztül majd akkor foglalunk
 * helyet a képpontoknak, amikor már tudjuk, hogy mennyi van. Továbbá, az
 * egyszerűbb helyfoglalás érdekében a helyet egyben foglaljuk le
 * (egydimenziós tömbként), így egy egyszerű int-re mutató pointert kapunk
 * (és nem int pointerre mutató pointert, mint valódi kétdimenziós foglalás
 * esetén). Vegyük észre, hogy a "P2" sztring a formátumhoz tartozik, így
 * nem kell tárolnunk. Egyrészt mindig ugyanaz, tehát felesleges, másrészt
 * az adatstruktúránk bármilyen szürkeárnyalatos képet képes tárolni, akkor
 * is, ha az pl. png formátumban van elmentve, mert az adattartalma akkor is
 * egy kép. (Az más kérdés, hogy a beolvasás kicsit bonyolultabb lenne.)
 */

typedef struct {
    int w;     // a kép szélessége
    int h;     // a kép magassága
    int m;     // a maximális képpont-intenzitás
    int *data; // a képpontok intenzitás-értékei
} kep_t;

/* Haladjunk modulárisan. Szükség lesz egy PGM formátumú kép betöltésére.
 * Erre készítsünk egy függvényt, ami a standard inputról olvas, és egy
 * képpel (kep_t) tér vissza. Nem csinálunk hibaellenőrzést, feltételezzük,
 * hogy egy (kommentek nélküli) PGM képet kapunk.
 */

kep_t beolvas(const char * fname) {
    int n, i;
    FILE *f;
    kep_t retval = {0, 0, 0, NULL};
    if ((f = fopen(fname, "r")) == NULL) {
        fprintf(stderr, "Hiba a kép beolvasása közben!\n");
        return retval;
    }
    fscanf(f, "P2\n");           // Beolvassuk az első sort, ez fix tartalmú.
    fscanf(f, "%d %d", &retval.w, &retval.h); // A kép szélessége, magassága.
    fscanf(f, "%d", &retval.m);            // A maximális képpont-intenzitás.
    /* Most, hogy ismerjük a kép méreteit, már tudunk helyet foglalni a
     * képpontok számára.
     */
    n = retval.w * retval.h;        // Képpontok száma, egy segédváltozóban.
    retval.data = malloc(n * sizeof(int));
    // Ezután már be tudjuk olvasni a képpontokat, sorfolytonosan.
    for (i = 0; i < n ; ++i) {
        fscanf(f, "%d", &retval.data[i]);
    }
    fclose(f);
    /* Megjegyeznénk, hogy a whitespace karakterekkel -- mint például a
     * sortörések -- nem kellett külön foglalkozni, mert a %d specifikáció
     * beolvasásnál automatikusan továbblép ezeken.
     */
    return retval;
}

/* Mivel a kép létrehozásakor dinamikusan foglaltunk memóriát, szükség lesz
 * ennek a felszabadítására. Erre is csináljunk egy függvényt.
 * Megjegyeznénk, hogy a függvény a kép adatain (a képpontok törlésén kívül)
 * nem változtat, vagyis azt, hogy egy kép törölve volt-e, futás közben
 * utólag így már lehetetlen megállapítani. Tehát a mi (programozói)
 * felelősségünk, hogy egy törölt képen már ne végezzünk semmilyen
 * műveletet.
 */

void torol(kep_t kep) {
    free(kep.data);
}

/* Beolvasás (és egyben létrehozás) valamint törlés megvolt, jöhet a kiírás,
 * szintén külön függvényben. Ez a beolvasás "tükörképe" lesz.
 */

void kiir(const char * fname, kep_t kep) {
    int n, i;
    FILE *f;
    if ((f = fopen(fname, "w")) == NULL) {
        fprintf(stderr, "Hiba a kép kiírása közben!\n");
        return;
    }
    fprintf(f, "P2\n");                 // Kiírjuk a formátum első sortát,
    fprintf(f, "%d %d\n", kep.w, kep.h);// a kép szélességét, és magasságát,
    fprintf(f, "%d", kep.m);            // a maximális képpont-intenzitást.
    n = kep.w * kep.h;                  // Képpontok száma.
    for (i = 0; i < n ; ++i) {
        /* A lenti kiírásban a trükk: a kiírt számokat szóközökkel
         * választjuk el, de minden képsor elején (amikor az index osztható
         * a kép szélességével) a szóköz helyett egy sorvége karaktert írunk
         * ki, így minden képsor adatai a kiírásban is új sorban fognak
         * kezdődni.
         */
        fprintf(f, "%c%d", (i % kep.w) ? ' ' : '\n', kep.data[i]);
    }
    fprintf(f, "\n");                   // Az utolsó sor lezárása.
    fclose(f);
    /* Megjegyeznénk, hogy a whitespace karakterek helyzete, vagyis hogy az
     * output hány sor (a kötelező elsőn túl), a saját döntésünk.
     */
}

/* Már csak az invertálás maradt hátra. Ez nem túl nehéz feladat, minden
 * képpontra függetlenül végrehajtható, és csak a képpont eredeti értékétől
 * (meg persze a maximális képpont-intenzitástól függ). A kapott képen
 * fogunk dolgozni. Mivel a kép direkt adatait nem változtatjuk (csak a
 * pointer által mutatott adatokat), a képet elegendő érték szerint átvenni.
 */

void invertal(kep_t kep) {
    int n, i;
    n = kep.w * kep.h;                  // Képpontok száma.
    for (i = 0; i < n ; ++i) {
        kep.data[i] = kep.m - kep.data[i];
    }
}

/* Már csak a főprogram van hátra. */

int main() {
    kep_t kep;
    kep = beolvas("input.pgm");
    invertal(kep);
    kiir("output.pgm", kep);
    torol(kep);
    return 0;
}

Feladatok

Feladat (f0177)

Feladat:

Készíts egy-egy olyan függvényt, ami beolvas,

  1. 8-as,
  2. 10-es vagy
  3. 16-os

számrendszerből átvált

  1. 8-as,
  2. 10-es vagy
  3. 16-os

számrendszerbe, majd kiír egy

  1. unsigned char,
  2. unsigned short,
  3. unsigned int,
  4. unsigned long vagy
  5. unsigned long long

típuson ábrázolt egész értéket. A teljes műveletet beolvasástól kiírásig egyetlen függvény végezze (ez összesen 45 darab függvény megírását jelenti). Írj ezekhez egy főprogramot, ami a felhasználó döntése alapján meghívja valamelyiket.

Feladat (f0178)

Feladat:

Írj egy programot, ami egy táblázatban bemutatja mi történik, ha a char, short, int, long és long long típusú értéket a hh, h, l és ll módosítókkal vagy ezek nélkül íratunk ki. (A bemutató akkor lesz látványos, ha az adott típushoz olyan értéket választunk, ami a nála egyel kisebb típus értékkészletébe már nem fér bele.)

Mi lehet a magyarázata annak, ha a program esetleg mégsem produkálja az elvárható hibás működést?

Feladat (f0179)

Feladat:

Írj egy programot, ami egy táblázatban bemutatja mi történik, ha a float, double és long double típusú értéket az l és L módosítókkal vagy ezek nélkül íratunk ki.

Mi lehet a magyarázata annak, ha a program esetleg mégsem produkálja az elvárható hibás működést?

Feladat (f0190)

Feladat:

Olvass be egy teljes sort egy sztringbe, majd írasd ki! A beolvasáshoz és a kiíráshoz a gets/fgets illetve puts/fputs függvényeket használd, minden lehetséges kombinációt próbálj ki! Mi a különbség a gets(...), puts(...) illetve fgets(stdin, ...), fputs(stdout, ...) használata között? Mit tapasztalsz, ha vegyesen használod őket (gets/fputs vagy fgets/puts)?

Feladat (f0199)

Problémafelvetés:

Írj egy programot, ami megmondja, hogy egy csoport hallgatói teljesítették-e a félévet programozás alapjai gyakorlatból.

Algoritmustervezés/Megvalósítás:

A főprogram csak az input/output műveleteket végezze, a számolást külön függvény(ek)ben oldd meg. Készíts egy adatszerkezetet, amiben egy hallgató nevét, pontjait tároljuk. A program tároljon külön minden részpontszámot és a hiányzásokat is. A program láncolt listával dolgozzon, a bemenet.txt nevű fájlból olvassa be az adatokat és a kimenet.txt nevű fájlba írja ki az eredményt. Az input végét a fájl vége jelezze. (A programot nem kell felkészíteni töredék adatokra, ha nincs vége a fájlnak, akkor a következő hallgató minden adata rendelkezésre áll benne.)

Feladat (f0235)

Problémafelvetés:

Olvass be egy PGM képet, és tedd fekete-fehérré, azaz a sötétebb pontokból csinálj feketét, a világosabbakból pedig fehéret.

Specifikáció:

A program inputja egy (egyszerűsített) PGM formátumú kép. A PGM formátum egy szürkeárnyalatos képet ír le a következő formában. A PGM fájl első sora a P2 szöveg; a második sortól kezdve egész értékeket tartalmaz whitespace karakterekkel (szóköz, tabulátor, sortörés, stb.) elválasztva. Az első két érték X és Y, a kép szélessége és magassága, a harmadik M, a képpontok lehetséges maximális értéke. Ezt további X*Y darab [0..M] intervallumba eső érték követi, az egyes képpontok intenzitásértékei, sorfolytonosan megadva. A kimenet is egy PGM formátumú kép, az eredeti kép fekete-fehérré alakított változata (amelyben így csak 0 és M intenzitású képpontok szerepelnek).

A képet az input.pgm fájlból kell beolvasni és az output.pgm fájlba kell kiírni.

Példa input fájlok: torony-o.pgm, progalap.pgm.

További feladatok

Feladat (f0152)

Feladat:

Írj egy programot, ami kiírja, hogy hány bitet foglalnak a C nyelv char, short, int, long, long long, float, double, és long double típusai!

Specifikáció:

A programnak nincs inputja, a kimenete pedig a megadott típusok által lefoglalt memória mérete bitekben. Egy sor elején a típusnév, majd a sor végén a típus által foglalt bitek száma, helyiérték szerinti egymás alá igazítva.

Algoritmustervezés/Megvalósítás:

A sizeof() operátor visszaadja, hogy hány bájtot foglal egy típus.

Feladat (f0155)

Feladat:

Írasd ki a 64 és 95 közé eső kódú karaktereket, valamint az 'a' és 'z' közé eső karakterek kódjait.

Specifikáció:

A programnak nincs bemenete. A kimenete kettő sor. Az elsőben a 64-es és 95-ös kódú karakterek közé eső karakterek vannak kiíratva (a 64-es és 95-ös kódú karaktereket is beleértve) közvetlenül egymás mögött. A második sorban az 'a' és 'z' közé eső karakterek ('a'-t és 'z'-t is beleértve) kódjai vannak kiíratva, egy-egy szóközzel elválasztva egymástól.

Feladat (f0156)

Feladat:

Készíts egy programot, amely bemutatja a különbséget a signed char és unsigned char értékkészlete között! Futtasd a programot, és figyeld meg az egyes értékek tárolhatóságát, illetve a két típus különbségét!

Specifikáció:

A programnak nincs bemenete. A kimenete több sor, soronként 3 értékkel: egy egész szám, ezen szám signed char típusúként értelmezett, illetve unsigned char típusúként értelmezett értéke. Az egész szám fedje le a signed és unsigned char típusok értékkészletének unióját, azaz -128-tól 255-ig menjen. A különböző sorokban kiírt értékek helyiérték szerint legyenek egymás alá igazítva.

Algoritmustervezés/Megvalósítás:

A ciklusban olyan ciklusváltozót használjunk, amelyben a -128 .. 255 értékek gond nélkül ábrázolhatóak (pl. int). A ciklusmagban a ciklusváltozó értékét adjuk át egy-egy, az említett típusú változónak, és a megfelelő helyeken ezek értékeivel dolgozzunk.

Feladat (f0157)

Feladat:

Készíts egy programot, amely bemutatja a túlcsordulás és alulcsordulás jelenségét a különféle méretű int típusok signed és unsigned változatainak tekintetében!

Algoritmustervezés/Megvalósítás:

Deklarálj 8 változót (signed/unsigned, short/int/long/long long), 0 kezdőértékkel, vonj ki belőlük egyet, majd írasd ki az értékeiket. Add értékül a változóknak a legnagyobb előjelesen ábrázolható értéket és adj hozzájuk egyet, majd ezt is írasd ki.

Lehetséges megoldás (m0157.c)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 *
 * Programozás Alapjai feladat megoldása
 *
 * Gergely Tamás, 2008. őszi félév.
 *
 * fordítás:
 *    gcc -o m0157 m0157.c
 *
 * futtatás:
 *    ./m0157
 */

#include <stdio.h>
#include <limits.h>

int main() {
    signed short int   ssi = 0;
    unsigned short int usi = 0;
    signed int         s_i = 0;
    unsigned int       u_i = 0;
    signed long int    sli = 0;
    unsigned long int  uli = 0;
    signed long long   sll = 0;
    unsigned long long ull = 0;
    ssi -= 1;
    usi -= 1;
    s_i -= 1;
    u_i -= 1;
    sli -= 1;
    uli -= 1;
    sll -= 1;
    ull -= 1;
    printf("0 mínusz 1\n");
    printf("    signed short     int: %22hd\n",  ssi);
    printf("  unsigned short     int: %22hu\n",  usi);
    printf("    signed           int: %22d\n",   s_i);
    printf("  unsigned           int: %22u\n",   u_i);
    printf("    signed long      int: %22ld\n",  sli);
    printf("  unsigned long      int: %22lu\n",  uli);
    printf("    signed long long int: %22lld\n", sll);
    printf("  unsigned long long int: %22llu\n", ull);
    ssi = usi = SHRT_MAX;
    s_i = u_i = INT_MAX;
    sli = uli = LONG_MAX;
    sll = ull = LLONG_MAX;
    printf("\nLegnagyobb ábrázolható előjeles szám...\n");
    printf("    signed short     int: %22hd\n",  ssi);
    printf("  unsigned short     int: %22hu\n",  usi);
    printf("    signed           int: %22d\n",   s_i);
    printf("  unsigned           int: %22u\n",   u_i);
    printf("    signed long      int: %22ld\n",  sli);
    printf("  unsigned long      int: %22lu\n",  uli);
    printf("    signed long long int: %22lld\n", sll);
    printf("  unsigned long long int: %22llu\n", ull);
    ssi += 1;
    usi += 1;
    s_i += 1;
    u_i += 1;
    sli += 1;
    uli += 1;
    sll += 1;
    ull += 1;
    printf("\n... plusz 1\n");
    printf("    signed short     int: %22hd\n",  ssi);
    printf("  unsigned short     int: %22hu\n",  usi);
    printf("    signed           int: %22d\n",   s_i);
    printf("  unsigned           int: %22u\n",   u_i);
    printf("    signed long      int: %22ld\n",  sli);
    printf("  unsigned long      int: %22lu\n",  uli);
    printf("    signed long long int: %22lld\n", sll);
    printf("  unsigned long long int: %22llu\n", ull);
    return 0;
}
Feladat (f0158)

Feladat:

Készíts egy programot, amely bemutatja a float, double és long double típusok pontossága közötti különbséget. Vizsgáld meg, melyik milyen relatív pontossággal dolgozik.

Algoritmustervezés/Megvalósítás:

Deklarálj két-két float, double és long double változót, amikben egy összeget, illetve egy különbséget tárolsz. Az összeget 0-ról, a különbséget 1-ről indítsd. (Típusazonos konstansokat, vagyis a 1.0f, 1.0 és 1.0L literálokat használd!) Készíts egy ciklust amely lefut N-szer (ahol N egy tetszőleges konstans, de érdemes 20 körülinek választani), minden iterációban az összeget növeli a különbséggel, a különbséget pedig osztja tízzel (itt a /= 10.0f, /= 10.0 és /= 10.0L, vagy *= 0.1f, *= 0.1 és *= 0.1L kifejezéseket illetve literálokat érdemes használni), majd kiírja az összegek aktuális értékét a választott N tizedesjegy pontossággal.

Lehetséges megoldás (m0158.c)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 *
 * Programozás Alapjai feladat megoldása
 *
 * Gergely Tamás, 2008. őszi félév.
 *
 * fordítás:
 *    gcc -o m0158 m0158.c
 *
 * futtatás:
 *    ./m0158
 */

#include <stdio.h>

int main() {
    int i;
    float       f = 0.0f, df = 1.0f;
    double      d = 0.0,  dd = 1.0;
    long double l = 0.0L, dl = 1.0L;
    printf("   %25s  %25s  %25s\n", "float", "double", "long double");
    for (i = 0; i < 22; ++i) {
        f += df;
        d += dd;
        l += dl;
        df *= 0.1f;
        dd *= 0.1;
        dl *= 0.1L;
        printf("   %25.22f; %25.22lf; %25.22Lf\n", f, d, l);
    }
    return 0;
}
Feladat (f0159)

Feladat:

Készíts egy programot, amely bemutatja a float, double és long double típusok értékkészlete közötti különbségeket, az ábrázolható legkisebb és legnagyobb abszolút érték nagyságrendje tekintetében. Hatványozz egy egynél kisebb pozitív számot amíg nullává nem válik, illetve szorozgass egy egynél nagyobbat, amíg végtelen nem lesz. Az eredményt minden lépés után mindhárom típusra írasd ki. Milyen értékeket kapsz lépésenként?

Algoritmustervezés/Megvalósítás:

Deklarálj egy-egy float, double és long double változót 0.1-es kezdőértékkel, majd szorozgasd mindet önmagával, amíg mindhárom 0 nem lesz. Közben írasd ki az értéküket, exponens alakú kiírást használva. Tedd meg ugyanezt, csak 10.0-es kezdőértékkel amíg mindhárom egyenlő nem lesz a végtelennel (ami 1.0/0.0). (Típusazonos konstansokat, vagyis 0.0f, 0.0 illetve 0.0L alakú literálokat használj!)

Lehetséges megoldás (m0159.c)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 *
 * Programozás Alapjai feladat megoldása
 *
 * Gergely Tamás, 2008. őszi félév.
 *
 * fordítás:
 *    gcc -o m0159 m0159.c
 *
 * futtatás:
 *    ./m0159
 */


#include <stdio.h>

int main() {
    float       f = 0.1f;
    double      d = 0.1;
    long double l = 0.1L;
    printf("Minimum:\n");
    printf("   %-15s  %-15s  %-15s\n", "float", "double", "long double");
    do {
        printf("   %15.7g  %15.7lg  %15.7Lg\n", f, d, l);
        f *= f;
        d *= d;
        l *= l;
    } while ((f != 0.0f) || (d != 0.0) || (l != 0.0L));
    f = 10.0f;
    d = 10.0;
    l = 10.0L;
    printf("Maximum:\n");
    printf("   %-15s  %-15s  %-15s\n", "float", "double", "long double");
    do {
        f *= f;
        d *= d;
        l *= l;
        printf("   %15.7g  %15.7lg  %15.7Lg\n", f, d, l);
    } while ((f != 1.0f / 0.0f) || (d != 1.0 / 0.0) || (l != 1.0L / 0.0L));
    return 0;
}
Feladat (f0162)

Feladat:

Készíts egy programot, amely beolvas egy valós és egy egész értéket, majd a valós értéket kiírja az egészben megadott abszolút pontossággal (vagyis annyi tizedesjeggyel a végén, amennyi az egész érték).

Feladat (f0163)

Feladat:

Készíts egy programot, amely beolvas egy valós és egy egész értéket, majd a valós értéket kiírja az egészben megadott relatív pontossággal (vagyis a legnagyobb helyiértékű számjeggyel együtt annyi "értékes" számjegye legyen, amennyi az egész érték, pl. a 123.456789-et 5 jegy pontossággal "123.46", "1.2346e2", vagy valami hasonló formában).

Feladat (f0180)

Problémafelvetés:

Készíts kódtáblázatot, amiben 32-től 127-ig soronként megjelennek az adott kódú karakterek és azok kódja oktális, decimális és hexadecimális alakban is.

Specifikáció:

A programnak nincs inputja. Az output egy táblázat, melynek egy-egy sora a következő alakú:

'A' 101 65 x41

Vagyis az oszlopokat 2-2 szóköz választja el, mindegyik oszlop 3 karakternyi széles, az első oszlop maga a karakter két aposztróf között, a második harmadik és negyedik oszlop a karakter kódja oktális, decimális és hexadecimális alakban, az oktális alak 3 számjegy vezető nullákkal, a decimális alak legfeljebb 3 karakter vezető nullák nélkül de 3 szélességben jobbra igazítva, a hexadecimális alak pedig egy 'x' karakter és 2 hexadecimális számjegy (nagybetűs).

Feladat (f0188)

Feladat:

Mi a különbség a %g, %f, %e kiíratási formák között? Készíts egy programot, ami ugyanazt a valós értéket mindhárom formátum segítségével kiírja, és nézd meg a különbséget. Többféle értéket is próbálj ki!

Feladat (f0198)

Problémafelvetés:

Írj egy programot, ami megmondja, hogy egy csoport hallgatói teljesítették-e a félévet programozás alapjai gyakorlatból.

Algoritmustervezés/Megvalósítás:

A főprogram csak az input/output műveleteket végezze, a számolást külön függvény(ek)ben oldd meg. Készíts egy adatszerkezetet, amiben egy hallgató nevét, pontjait tároljuk. A program tároljon külön minden részpontszámot és a hiányzásokat is. A program láncolt listával dolgozzon, ahol az input végét egy speciális hallgatónév (például "*") jelezze.

Feladat (f0232)

Problémafelvetés:

Olvass be egy PGM képet, és tedd fekete-fehérré, azaz a sötétebb pontokból csinálj feketét, a világosabbakból pedig fehéret.

Specifikáció:

A program inputja egy (egyszerűsített) PGM formátumú kép. A PGM formátum egy szürkeárnyalatos képet ír le a következő formában. A PGM fájl első sora a P2 szöveg; a második sortól kezdve egész értékeket tartalmaz whitespace karakterekkel (szóköz, tabulátor, sortörés, stb.) elválasztva. Az első két érték X és Y, a kép szélessége és magassága, a harmadik M, a képpontok lehetséges maximális értéke. Ezt további X*Y darab [0..M] intervallumba eső érték követi, az egyes képpontok intenzitásértékei, sorfolytonosan megadva. A kimenet is egy PGM formátumú kép, az eredeti kép fekete-fehérré alakított változata (amelyben így csak 0 és M intenzitású képpontok szerepelnek).

Példa input fájlok: torony-o.pgm, progalap.pgm.

Feladat (f0233)

Problémafelvetés:

Tükrözz egy PGM képet a függőleges tengelyére!

Specifikáció:

A program inputja egy (egyszerűsített) PGM formátumú kép. A PGM formátum egy szürkeárnyalatos képet ír le a következő formában. A PGM fájl első sora a P2 szöveg; a második sortól kezdve egész értékeket tartalmaz whitespace karakterekkel (szóköz, tabulátor, sortörés, stb.) elválasztva. Az első két érték X és Y, a kép szélessége és magassága, a harmadik M, a képpontok lehetséges maximális értéke. Ezt további X*Y darab [0..M] intervallumba eső érték követi, az egyes képpontok intenzitásértékei, sorfolytonosan megadva. A kimenet is egy PGM formátumú kép, az eredeti kép függőleges tengelyre tükrözött változata.

Példa input fájlok: torony-o.pgm, progalap.pgm.

Feladat (f0236)

Problémafelvetés:

Tükrözz egy PGM képet a függőleges tengelyére!

Specifikáció:

A program inputja egy (egyszerűsített) PGM formátumú kép. A PGM formátum egy szürkeárnyalatos képet ír le a következő formában. A PGM fájl első sora a P2 szöveg; a második sortól kezdve egész értékeket tartalmaz whitespace karakterekkel (szóköz, tabulátor, sortörés, stb.) elválasztva. Az első két érték X és Y, a kép szélessége és magassága, a harmadik M, a képpontok lehetséges maximális értéke. Ezt további X*Y darab [0..M] intervallumba eső érték követi, az egyes képpontok intenzitásértékei, sorfolytonosan megadva. A kimenet is egy PGM formátumú kép, az eredeti kép függőleges tengelyre tükrözött változata.

A képet az input.pgm fájlból kell beolvasni és az output.pgm fájlba kell kiírni.

Példa input fájlok: torony-o.pgm, progalap.pgm.

Feladat (f0258)

Problémafelvetés:

Szükségünk lenne egy programra, amely kiszámolja egy adott sugarú körbe illetve gömbbe illeszkedő szabályos síkbeli illetve térbeli alakzatok kerületét, területét, felszínét, illetve térfogatát.

Specifikáció:

A program inputja egyetlen valós szám, a gömb sugara, amelyet parancssori paraméterként kap meg. A program outputja a szabályos síkbeli illetve térbeli alakzatok jellemző értékeinek listája. Az ismerendő síkidomok illetve testek (nem teljes) listája: kör, háromszög, négyzet, öt- és hatszög, tetraéder, kocka, oktaéder, dodekaéder, ikozaéder.

Algoritmustervezés/Megvalósítás:

A program függvénypointerekkel dolgozzon. Legyen egy tömb, melynek elemei struktúrák, amik egy sztringet és egy függvénypointert tartalmaznak. A sztring mondja meg, hogy mit számol ki a függvény. Ezek után a főprogram egyetlen ciklus, ami végigmegy a tömb elemein, kiírja a sztringet és a pointeren keresztül meghívja a függvényt a megadott paraméterrel.

Feladat (f0259)

Feladat:

Olvassuk be egy tömb elemeit, írassuk ki az elemeket, duplázzuk meg, majd ismét írassuk ki őket. Mindezt úgy, hogy a tömb bejárását egy bejar nevű függvény végezze, ami megkapja a tömböt és a műveletet.

Kapcsolódó linkek


Utolsó frissítés: 2020-11-09 15:05:22