Kihagyás

5. gyakorlat

2. ZH

A gyakorlat elején 25 percben kerül megírásra a 2. zh. A feladat(ok) témái:

  • függvények megírása, bemenet/kimenet kezelés nélkül
  • vezérlési szerkezetek használata

A zh-t a bíró rendszeren keresztül kell leadni az óra időpontjában.

Struktúra és unió

A struct és a union is adatok/változók gyűjteményeként, kollekciójaként fogható fel. Mindkettőnek mezői vannak, melyekben többfajta típusú adat tárolható. Alapvető különbség viszont, hogy a struct egyszerre tudja az összes mező értékét tárolni, míg a union egyszerre csak egy mező értékét tárolja (azét, amelyik legutóbb értéket kapott).

A mezők olyan viszonyban vannak a teljes struktúrával/unióval, mint a tömbelemek a teljes tömbbel. Csak míg a tömbelemek egyforma típusúak és egy index segítségével hivatkozhatók, addig a struktúra/unió elemei különböző típusúak lehetnek és mezőnévvel tudunk rájuk hivatkozni.

Struktúra

Egy struktúrában tárolhatunk több különböző féle adattípust, kezelhetjük őket külön, de egyetlen egységként is. Egy struktúra definiálásakor adhatunk neki egy nevet, és meg kell adnunk a benne lévő mezők típusát és neveit is (mintha csak "belső" változókat deklarálnánk). Például ha létre akarunk hozni egy struktúrát a komplex számok tárolására, azt megtehetjük így:

1
2
3
4
struct komplex {
    double valos;
    double kepzetes;
};

Amennyiben el akarjuk érni a struktúránk egyik mezőjét, a . operátort kell használnunk.

1
2
3
4
5
6
int main() {
    struct komplex z;
    z.valos = 0.7071;
    z.kepzetes = -0.7071;
    return 0;
}

A tömbökkel ellentétben egy struct típus megengedett függvény visszatérési értékeként is:

1
2
3
4
5
struct komplex dupla(struct komplex z) {
    z.valos = z.valos*2;
    z.kepzetes = z.kepzetes*2;
    return z;
}

A fenti példában a függvény paramétere is struktúra volt, de paraméterként a tömb is megengedett, így az nem különbség.

Vegyük észre, hogy a típus neve struct komplex, vagyis így együtt kellett használni mindenhol. A struct modnja meg, hogy ez egy ilyen jellegű összetett típus, a struktúranév pedig a pontos belső szerkezetét azonosítja.

Az alábbi videók a struktúrákat mutatják be:

struct_1

struct_2

Példák

Egy könnyebb, szemléltető feladaton ki is tudjuk próbálni, hogyan működik:

Feladat (f0181)

Problémafelvetés:

Írj egy programot ami három oldalhosszból kiszámítja egy háromszög kerületét és területét!

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

A számítást egyetlen függvény végezze, a főprogram csak a bemenet-kimenetért feleljen. A terület és kerület tárolására hozz létre egy struct adattípust, a számítást végző függvény egy ilyen típusú értékkel térjen vissza.

Megjegyzés

C-ben egy függvény egyetlen értékkel tud csak visszatérni. Ez az érték viszont lehet struktúra típusú, ami akárhány és akármilyen típusú mezőt tartalmazhat. Vagyis, technikailag C-ben is megoldható a több értékkel való visszatérés, bár ilyen esetekben inkább kimenő paramétereket szokás használni, mint azt majd a pointereknél látjuk.

Ötlet

Mivel egy összetett típusú érték megadása szintaktikailag nem annyira triviális, érdemes a függvényben a visszatérési érték tárolására létrehozni egy változót, beállítani ennek mezőit, majd a változó értékével visszatérni.

A háromszög kerületének kiszámolása az \(a\), \(b\) és \(c\) oldalhosszak alapján reméljük nem okoz fejtörést. A kerület feléből (\(s\)) a Héron-képlet (\(T=\sqrt{s(s-a)(s-b)(s-c)}\)) segítségével ki tudjuk számolni a területet.

Lehetséges megoldás (m0181.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
/*
 * 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/Megvalósítás*:
 *    A számítást egyetlen függvény végezze, a főprogram csak a bemenet-
 *    kimenetért feleljen. A terület és kerület tárolására hozz létre egy
 *    struct adattípust, a számítást végző függvény egy ilyen típusú
 *    értékkel térjen vissza.
 *
 * fordítás:
 *    gcc -o m0181 m0181.c -lm
 *
 * futtatás:
 *    ./m0181
 */

#include <math.h>
#include <stdio.h>

struct haromszog {
    double kerulet;
    double terulet;
};

struct haromszog szamolas(double a, double b, double c) {
    struct haromszog retval;    // Mivel a visszatérési érték egy összetett típus lesz,
    double s;                   // egyszerűbb egy változóban összerakni ezt az értéket,
    retval.kerulet = a + b + c;
    s = retval.kerulet / 2.0;
    retval.terulet = sqrt(s * (s-a) * (s-b) * (s-c));
    return retval;              // és a végén a változó értékével visszatérni.
}

int main() {
    double a, b, c;
    struct haromszog adat;
    printf("Kérem a három oldal hosszát: ");
    scanf("%lf %lf %lf", &a, &b, &c);
    adat = szamolas(a, b, c);
    printf("Kerület: %lf\nTerület: %lf\n", adat.kerulet, adat.terulet);
    return 0;
}

Illetve egy picit belegondolva ezt is meg tudjuk oldani:

Feladat (f0182)

Problémafelvetés:

Írj egy programot ami három kétdimenziós koordináta-párból kiszámítja egy háromszög kerületét és területét!

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

Az érdemi számításokat függvények végezzék, a főprogram csak a bemenet-kimenetért feleljen. A kétdimenziós pont tárolására, valamint a terület és kerület együttes tárolására is hozz létre struct adattípusokat, és a számítást végző függvények ezeket használják (ahol ez lehetséges).

Ötlet

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

Beolvasáskor a scanf segítségével a struktúra mezőibe mint változókba tudunk beolvasni értékeket (vagyis nem kell külön változóba beolvasni, és azt utána átadni a struktúra megfelelő mezőjének).

A háromszög három oldalát egy-egy pontpár távolságából tudjuk kiszámolni.

Két pont távolságának kiszámolására érdemes külön függvényt csinálni, ez a Pitagorasz tétel alapján a pontok x és y koordinátáinak páronként vett különbségei négyzetösszegének négyzetgyökével fog visszatérni.

A háromszög jellemzőinek kiszámolásához tehát először a pontokból meghatározzuk az \(a\), \(b\) és \(c\) oldalhosszakat, ezekből pedig az előző feladat szamolas függvénye már ki tudja számolni azt, amit kell.

Lehetséges megoldás (m0182.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
/*
 * 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/Megvalósítás*:
 *    Az érdemi számításokat függvények végezzék, a főprogram csak a
 *    bemenet-kimenetért feleljen. A kétdimenziós pont tárolására, valamint
 *    a terület és kerület együttes tárolására is hozz létre struct
 *    adattípusokat, és a számítást végző függvények ezeket használják (ahol
 *    ez lehetséges).
 *
 * fordítás:
 *    gcc -o m0182 m0182.c -lm
 *
 * futtatás:
 *    ./m0182
 */

#include <math.h>
#include <stdio.h>

struct pont {
    double x;
    double y;
};

struct haromszog {
    double kerulet;
    double terulet;
};

double tavolsag(struct pont p, struct pont q) {
    double dx, dy;
    dx = p.x - q.x;
    dy = p.y - q.y;
    return sqrt(dx*dx + dy*dy);
}

struct haromszog szamolas(double a, double b, double c) {
    struct haromszog retval;
    double s;
    retval.kerulet = a + b + c;
    s = retval.kerulet / 2.0;
    retval.terulet = sqrt(s * (s-a) * (s-b) * (s-c));
    return retval;
}

int main() {
    struct pont a, b, c;
    struct haromszog adat;
    printf("Kérem a három pont koordinátáit ax ay bx by cx cy sorrendben:\n");
    scanf("%lf %lf %lf %lf %lf %lf", &a.x, &a.y, &b.x, &b.y, &c.x, &c.y);
    adat = szamolas(tavolsag(a, b), tavolsag(b, c), tavolsag(c, a));
    printf("Kerület: %lf\nTerület: %lf\n", adat.kerulet, adat.terulet);
    return 0;
}

A specifikációtól eltérő, de ugyanazt az alapproblémát megoldó program:

1
[![traingle struct][yvi_triangle_struct]][ytv_triangle_struct]

Unió

Az union egy kicsit máshogy működik mint a struct. Szintaktikailag nagyon hasonlít a struct-ra, hasonlóan tudjuk kezelni, de ha felülírjuk az egyik adattagjának az értékét, azzal az összes többi adattag értékét "töröljük", definiálatlanná tesszük. A union létrehozása olyan, mint a struct-é, csak a kulcsszó más:

1
2
3
4
union numbers {
    int egesz;
    bouble valos;
};

Az alábbi videó az unió tulajdonságait mutatja be:

str_rev

Egymásba ágyazás

Ezeket a típusokat egymásba is lehet ágyazni, például:

1
2
3
4
5
6
7
8
9
union reg {
    struct {
        unsigned char byte1;
        unsigned char byte2;
        unsigned char byte3;
        unsigned char byte4;
    } bytes;
    unsigned int dword;
}

Tehát el tudunk menteni egy (a jelenleg használt architektúrákon valószínűleg) 4 byte-os unsigned int értéket a dword mezőben vagy 4 darab 1 byte-os unsigned char értéket a bytes nevű struct típusú mezőben.

Megjegyzés

A mezők fizikai tárolási módja miatt (az union összes mezője "ugyanott" tárolodik a memóriában, ezért is tudja csak az egyik értékét megjegyezni) ha a union bármelyik mezőjének (bytes vagy dword) értéket adunk, akkor a másik mező is "kap egy értéket" (mivel a tárolásra használt bitek ugyanazok, de legalábbis átfednek). Ennek az "új értéknek" az értelmezése jelentősen eltérhet a másik mező szerinti értelmezéstől. Mivel itt mindkét mező összesen ugyanakkora (4-szer 1, illetve 1-szer 4 bájt), a bytes-ban pontosan a dword egyes bájtjait érjük el.

Feladat

Hozzunk létre egy struct-ot és egy union-t ugyanazokkal az adatagokkal. Adjunk értéket mindkettő első adattagjának és nézzük meg az adattagok értékeit. Adjunk értéket a második adattagoknak is, majd megint nézzük meg az adattagok értékeit. Mit tapasztalunk?

Lehetséges válaszok

Az első értékadások után a struct és union első adattagjából is ugyanúgy ki tudjuk olvasni a belerakott értéket, a második adattagok pedig látszólag valamilyen véletlen értékeket tartalmaznak. Az újabb értékadás után a struct mindkét mezőjének értéke az maradt, amit annak a mezőnek értékül adtunk, a union első mezője viszont látszólag valami véletlenszerű értéket tartalmaz.

Ha a két mezőt ugyanolyan típusúnak deklaráltuk, akkor a union mindkét mezőjében az előtte bármely mezőnek adott értéket találjuk. Az azonos típusú mezők a struct típus viselkedésében nem okoznak ilyen eltérést.

Típusdefiníció

Van lehetőségünk új típusokat is definiálni. Az erre használatos kulcsszó a typedef. A definiálás módja:

typedef típus név;

Létrehozhatunk például egy vector adattípust, 3 dimenziós valós térbeli helyvektor tárolására!

Figyelj!

Dimenziószám és dimenziószám között lehetnek különbségek. A három dimenziós térbeli helyvektor az adattípus magas szintű (szaktudományos, matematikai) megfogalmazása, azt jelenti, hogy három egymástól független értéket kell tárolnunk. Három egymástól független értéket absztrakt adattípusként egy három elemű egydimenziós tömbben tudunk tárolni. Vagyis a dimenziószám jelentése a "szövegkörnyezet" függvényében eltérhet.

1
typedef double vector[3];

Létrehozhatunk egy u16 adattípust előjeltelen unsigned short int-ek tárolására:

1
typedef unsigned short int u16;

Vagy épp struktúrát is, ilyen módon:

1
2
3
4
typedef struct {
    char nev[32];
    int eletkor;
} hallgato;

Vegyük észre a különbséget a korábbi struktúra definíció és a mostani típusdefiníció között. Itt a hallgato nem csak egy struktúra elnevezése (ami megkülönbözteti ezt a struktúrát a többi struktúrától), hanem egy teljes típusé, így a struct kulcsszó nélkül, önállóan használható, például az alábbi módon:

1
2
3
hallgato idosebb(hallgato h1, hallgato h2) {
    return h1.eletkor >= h2.eletkor ? h1 : h2;
}

Videó a typedef használatáról:

typedef

A következő feladatban használjunk típusdefiníciót!

Feladat (f0200)

Problémafelvetés:

Írj egy programot, ami kiszámolja, majd irányszöggel és nagysággal megadja a hasonlóképpen megadott fizikai erők sorozatának eredőjét a kétdimenziós térben. Az erők sorozatának végét egy 0 nagyságú erő jelzi.

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. Az adatok tárolására használj összetett adatszerkezetet, ha van értelme.

Ötlet

Egy kétdimenziós vektort kétféleképpen lehet megadni: irányszög és nagyság párossal, vagy x és y koordináta-párral. A kettő egyértelműen átalakítható egymásba a szinusz (sin) és koszinusz (cos), illetve az arkusz-tangens (atan - inverz tangens) függvények segítségével. Erre szükségünk is lesz, hiszen a bemenet és kimenet irányszög-nagyság vektorokkal dolgozik, vektorokat összeadni ugyanakkor az x-y koordinátapárral sokkal egyszerűbb.

Kell tehát egy-egy struct típus a kétféle tárolásra, és egy-egy függvény az oda-vissza konverzióra. És akkor már érdemes egy külön függvényt írni két x-y koordinátás vektor összeadására is. Ezekben a függvényekben a megfelelő struct típus használható visszatérési értékként.

Lehetséges megoldás (m0200.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
/*
 * 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/Megvalósítás:
 *    Kétféle főbb adattípusunk lesz, mindkettő a vektor reprezentálására. Az
 *    egyik a polárkoordináták (irányszög és nagyság, a szög fokokban), a
 *    másik a derékszögű koordinátarendszer (x és y koordináták) szerinti
 *    tárolásra. A konverziót az egyikről a másikra függvényekkel oldjuk meg.
 *    Az összegzéshez az (x,y) verziót használjuk (az egyszerűbb), és külön
 *    függvényt írunk rá.
 *
 * fordítás:
 *    gcc -o m0200 m0200.c -lm
 *
 * futtatás:
 *    ./m0200
 */

#include <math.h>
#include <stdio.h>

typedef struct {
    double x, y;
} vector_xy_t;

typedef struct {
    double irany, nagysag;
} vector_in_t;

vector_xy_t convert_to_xy(vector_in_t v) {
    vector_xy_t retval;
    retval.x = cos(v.irany * M_PI / 180.0) * v.nagysag;
    retval.y = sin(v.irany * M_PI / 180.0) * v.nagysag;
    return retval;
}

vector_in_t convert_to_in(vector_xy_t v) {
    vector_in_t retval;
    if (v.x == 0.0) {
        if (v.y == 0.0) {
            retval.irany = 0.0;
        } else {
            retval.irany = (v.y > 0.0) ? 90.0 : -90.0;
        }
    } else {
        retval.irany = atan(v.y/v.x) * 180.0 / M_PI;
        if (v.x < 0.0) {
            if (v.y < 0.0) {
                retval.irany -= 180.0;
            } else {
                retval.irany += 180.0;
            }
        }
    }
    retval.nagysag = sqrt(v.x*v.x + v.y*v.y);
    return retval;
}

vector_xy_t add(vector_xy_t a, vector_xy_t b) {
    vector_xy_t retval;
    retval.x = a.x + b.x;
    retval.y = a.y + b.y;
    return retval;
}

int main() {
    vector_in_t v_in;
    vector_xy_t v_xy, v_sum = { .x = 0.0, .y = 0.0 };
    scanf("%lf %lf", &v_in.irany, &v_in.nagysag);
    while (v_in.nagysag != 0.0) {
        v_xy = convert_to_xy(v_in);
        v_sum = add(v_sum, v_xy);
        scanf("%lf %lf", &v_in.irany, &v_in.nagysag);
    }
    v_in = convert_to_in(v_sum);
    printf("(%lf°, %lf)\n", v_in.irany, v_in.nagysag);
    return 0;
}

Fordítási hibák és figyelmeztetések

A fordítónk és felhasználói környezetünk segítségünkre van a program írása közben. Ha valami szintaktikai hiba csúszik a kódunkba, azt a fordító fordítás közben jelzi nekünk (és mellesleg nem készíti el a programot). (Egyes IDE-k, szövegszerkesztők akár már írás közben jelezhetik ezeket a hibákat.) Ezeket a hibaüzeneteket érdemes megismerni, és megtanulni értelmezni.

A következő feladatok tele vannak szintaktikai (ha úgy tetszik, "helyesírási") hibákkal, keressük meg hol vannak ezek és milyen hibaüzenetet kapunk velük!

Feladat (f0263)

Feladat:

A felszin.c program feladata lenne kiszámolni a térben négy pont által meghatározott test felszínét. A programban a fordítása során a fordító több hibát és figyelmeztetést is jelez. Javítsd ki ezeket!

 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
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 *
 * Programozás Alapjai
 */

#include <stdio.h>

struct pont {
    double x;
    double y;
    double z;
}

typedef struct pont pont_t;

int Main(int argc, char *argv[]) {
    pont_t P[4];
    double t[4];
    double felszin;
    if (argc < 5) {
        fprintf(stderr, "Használat: %c '(x1,y1,z1)' '(x2,y2,z2)' '(x3,y3,z3)' '(x4,y4,z4)'\n", argv[0]);
        return 1;
    }
    for (i = 0; i < 4; ++i) {
        P(i) = get_pont(argv[i+1]);
        t(i) = 0.0;
    }
    t[0] = terulet(P[1], P[2], P[3]);
    t[1] = terulet(P[0], P[2], P[3]);
    t[2] = terulet(P[0], P[1], P[3]);
    t[3] = terulet(P[0], P[1], P[2]);
    for (i = 0; i < 4; ++i) {
        if (t[i] = 0.0)
            printf("A megadott pontok egy síkba esnek.\n");
            return 1;
        }
    }
    for (i = 0; i < 4; ++i) {
        felszin += t{i};
    }
    printf("A test felszíne: A = %.3lf\n", felszin);
    return 0;
}

pont_t get_pont(const char *str) {
    pont_t retval = {0.0, 0.0, 0.0};
    sscanf(str, "(%lf,%lf,%lf)", retval.x, retval.y, retval.z);
}

double tavolsag[pont_t P, pont_t Q] {
    pont_t d;
    d.x = P.x - Q.x;
    d.y = P.y - Q.y;
    d.z = P.z - Q.z;
    return sqrt(d.x * d.x + d.y * d.y + d.z * d.z);
}

double terulet(pont_t A, pont_t B, pont_t C) [
    double a, b, c, s;
    a = tavolsag(B, C);
    b = tavolsag(A, C);
    c = tavolsag(A, B);
    s = (a + b + c) / 2.0;
    return sqrt(s * (s - a) * (s - b) * (s - c));
]
felszin.c

A gcc fordító kimenete
  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
felszin.c:17:9: error: expected ‘;’, identifier or ‘(’ before ‘struct’
   17 | typedef struct pont pont_t;
      |         ^~~~~~
felszin.c: In function ‘Main’:
felszin.c:20:5: warning: statement with no effect [-Wunused-value]
   20 |     pont_t P[4];
      |     ^~~~~~
felszin.c:20:11: error: expected ‘;’ before ‘P’
   20 |     pont_t P[4];
      |           ^~
      |           ;
felszin.c:24:39: warning: format ‘%c’ expects argument of type ‘int’, but argument 3 has type ‘char *’ [-Wformat=]
   24 |         fprintf(stderr, "Használat: %c '(x1,y1,z1)' '(x2,y2,z2)' '(x3,y3,z3)' '(x4,y4,z4)'\n", argv[0]);
      |                                      ~^                                                         ~~~~~~~
      |                                       |                                                             |
      |                                       int                                                           char *
      |                                      %s
felszin.c:27:10: error: ‘i’ undeclared (first use in this function)
   27 |     for (i = 0; i < 4; ++i) {
      |          ^
felszin.c:27:10: note: each undeclared identifier is reported only once for each function it appears in
felszin.c:28:9: warning: implicit declaration of function ‘P’ [-Wimplicit-function-declaration]
   28 |         P(i) = get_pont(argv[i+1]);
      |         ^
felszin.c:28:16: warning: implicit declaration of function ‘get_pont’ [-Wimplicit-function-declaration]
   28 |         P(i) = get_pont(argv[i+1]);
      |                ^~~~~~~~
felszin.c:29:9: error: called object ‘t’ is not a function or function pointer
   29 |         t(i) = 0.0;
      |         ^
felszin.c:21:12: note: declared here
   21 |     double t[4];
      |            ^
felszin.c:31:12: warning: implicit declaration of function ‘terulet’ [-Wimplicit-function-declaration]
   31 |     t[0] = terulet(P[1], P[2], P[3]);
      |            ^~~~~~~
felszin.c:31:20: error: ‘P’ undeclared (first use in this function)
   31 |     t[0] = terulet(P[1], P[2], P[3]);
      |                    ^
felszin.c:36:9: warning: this ‘if’ clause does not guard... [-Wmisleading-indentation]
   36 |         if (t[i] = 0.0)
      |         ^~
felszin.c:38:13: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the ‘if’
   38 |             return 1;
      |             ^~~~~~
felszin.c:22:12: warning: unused variable ‘felszin’ [-Wunused-variable]
   22 |     double felszin;
      |            ^~~~~~~
felszin.c: At top level:
felszin.c:41:5: error: expected identifier or ‘(’ before ‘for’
   41 |     for (i = 0; i < 4; ++i) {
      |     ^~~
felszin.c:41:19: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘<’ token
   41 |     for (i = 0; i < 4; ++i) {
      |                   ^
felszin.c:41:24: error: expected identifier or ‘(’ before ‘++’ token
   41 |     for (i = 0; i < 4; ++i) {
      |                        ^~
felszin.c:44:12: error: expected declaration specifiers or ‘...’ before string constant
   44 |     printf("A test felszíne: A = %.3lf\n", felszin);
      |            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
felszin.c:44:45: error: unknown type name ‘felszin’
   44 |     printf("A test felszíne: A = %.3lf\n", felszin);
      |                                             ^~~~~~~
felszin.c:45:5: error: expected identifier or ‘(’ before ‘return’
   45 |     return 0;
      |     ^~~~~~
felszin.c:46:1: error: expected identifier or ‘(’ before ‘}’ token
   46 | }
      | ^
felszin.c:48:1: error: unknown type name ‘pont_t’
   48 | pont_t get_pont(const char *str) {
      | ^~~~~~
felszin.c: In function ‘get_pont’:
felszin.c:49:5: warning: statement with no effect [-Wunused-value]
   49 |     pont_t retval = {0.0, 0.0, 0.0};
      |     ^~~~~~
felszin.c:49:11: error: expected ‘;’ before ‘retval’
   49 |     pont_t retval = {0.0, 0.0, 0.0};
      |           ^~~~~~~
      |           ;
felszin.c:50:34: error: ‘retval’ undeclared (first use in this function)
   50 |     sscanf(str, "(%lf,%lf,%lf)", retval.x, retval.y, retval.z);
      |                                  ^~~~~~
felszin.c: At top level:
felszin.c:53:23: error: expected ‘]’ before ‘P’
   53 | double tavolsag[pont_t P, pont_t Q] {
      |                       ^~
      |                       ]
felszin.c:61:16: error: expected declaration specifiers or ‘...’ before ‘pont_t’
   61 | double terulet(pont_t A, pont_t B, pont_t C) [
      |                ^~~~~~
felszin.c:61:26: error: expected declaration specifiers or ‘...’ before ‘pont_t’
   61 | double terulet(pont_t A, pont_t B, pont_t C) [
      |                          ^~~~~~
felszin.c:61:36: error: expected declaration specifiers or ‘...’ before ‘pont_t’
   61 | double terulet(pont_t A, pont_t B, pont_t C) [
      |                                    ^~~~~~
felszin.c:63:5: warning: data definition has no type or storage class
   63 |     a = tavolsag(B, C);
      |     ^
felszin.c:63:5: warning: type defaults to ‘int’ in declaration of ‘a’ [-Wimplicit-int]
felszin.c:63:9: warning: implicit declaration of function ‘tavolsag’ [-Wimplicit-function-declaration]
   63 |     a = tavolsag(B, C);
      |         ^~~~~~~~
felszin.c:63:18: error: ‘B’ undeclared here (not in a function)
   63 |     a = tavolsag(B, C);
      |                  ^
felszin.c:63:21: error: ‘C’ undeclared here (not in a function)
   63 |     a = tavolsag(B, C);
      |                     ^
felszin.c:64:5: warning: data definition has no type or storage class
   64 |     b = tavolsag(A, C);
      |     ^
felszin.c:64:5: warning: type defaults to ‘int’ in declaration of ‘b’ [-Wimplicit-int]
felszin.c:64:18: error: ‘A’ undeclared here (not in a function)
   64 |     b = tavolsag(A, C);
      |                  ^
felszin.c:65:5: warning: data definition has no type or storage class
   65 |     c = tavolsag(A, B);
      |     ^
felszin.c:65:5: warning: type defaults to ‘int’ in declaration of ‘c’ [-Wimplicit-int]
felszin.c:66:5: warning: data definition has no type or storage class
   66 |     s = (a + b + c) / 2.0;
      |     ^
felszin.c:66:5: warning: type defaults to ‘int’ in declaration of ‘s’ [-Wimplicit-int]
felszin.c:66:9: error: initializer element is not constant
   66 |     s = (a + b + c) / 2.0;
      |         ^
felszin.c:67:5: error: expected identifier or ‘(’ before ‘return’
   67 |     return sqrt(s * (s - a) * (s - b) * (s - c));
      |     ^~~~~~
felszin.c:68:1: error: expected identifier or ‘(’ before ‘]’ token
   68 | ]
      | ^
felszin.c: In function ‘Main’:
felszin.c:40:5: warning: control reaches end of non-void function [-Wreturn-type]
   40 |     }
      |     ^
felszin.c: In function ‘get_pont’:
felszin.c:51:1: warning: control reaches end of non-void function [-Wreturn-type]
   51 | }
      | ^
Megoldás, 1. lépés

1
2
3
felszin.c:17:9: error: expected ‘;’, identifier or ‘(’ before ‘struct’
   17 | typedef struct pont pont_t;
      |         ^~~~~~
A 17. sor előtt hiányzik egy ; karakter: praktikusan a 15. sorban nincs lezárva a struktúra-definíció.

1
2
3
4
5
6
7
8
felszin.c: In function ‘Main’:
felszin.c:20:5: warning: statement with no effect [-Wunused-value]
   20 |     pont_t P[4];
      |     ^~~~~~
felszin.c:20:11: error: expected ‘;’ before ‘P’
   20 |     pont_t P[4];
      |           ^~
      |           ;
Ezeket az előző hiba okozta: nem volt deklarálva a struktúra.

1
2
3
4
5
6
felszin.c:24:39: warning: format ‘%c’ expects argument of type ‘int’, but argument 3 has type ‘char *’ [-Wformat=]
   24 |         fprintf(stderr, "Használat: %c '(x1,y1,z1)' '(x2,y2,z2)' '(x3,y3,z3)' '(x4,y4,z4)'\n", argv[0]);
      |                                      ~^                                                         ~~~~~~~
      |                                       |                                                             |
      |                                       int                                                           char *
      |                                      %s
A 24. sorban rossz a konverziós specifkáció: sztringeket, mint ami az argv[0] is, a %s specifikációval kell kiíranti, nem a %c-vel.

1
2
3
4
felszin.c:27:10: error: ‘i’ undeclared (first use in this function)
   27 |     for (i = 0; i < 4; ++i) {
      |          ^
felszin.c:27:10: note: each undeclared identifier is reported only once for each function it appears in
A 27. sorban használt i változó nincs deklarálva. A gcc csak egyszer szól érte, pedig a 28., 29., 35., 36., 41. és 42. sorokban is használva van. Vagy for (int i = 0; ... módon deklaráljuk, de akkor a 35. és 41. sorban is így kell tenni, vagy a függvény elején (a 19. és 23. sorok közé beszúrva) int i; módon külön deklaráljuk.

1
2
3
felszin.c:28:9: warning: implicit declaration of function ‘P’ [-Wimplicit-function-declaration]
   28 |         P(i) = get_pont(argv[i+1]);
      |         ^
Ez egy félrevezető üzenet a 28. sorban: a gcc szerint a P egy függvény (mert látszólag meghívjuk), és panaszkodik, hogy nem deklaráltuk. De mi tudjuk, hogy a P egy tömb, tehát valójában a hiba az, hogy P(i) helyett P[i]-t kellene írni.

1
2
3
felszin.c:28:16: warning: implicit declaration of function ‘get_pont’ [-Wimplicit-function-declaration]
   28 |         P(i) = get_pont(argv[i+1]);
      |                ^~~~~~~~
Ugyanaz a hibaüzenet, mint az előbb, de itt, a get_pont-ra vonatkoztatva jogos: a függvényt korábban kellene deklarálni. Ezt megtehetjük egy pont_t get_pont(const char *str); sor beszúrásával a 19. sor elé, vagy a függvénydefiníció (48-51. sor) 19. sor elé mozgatásával.

1
2
3
4
5
6
felszin.c:29:9: error: called object ‘t’ is not a function or function pointer
   29 |         t(i) = 0.0;
      |         ^
felszin.c:21:12: note: declared here
   21 |     double t[4];
      |            ^
A 29. sorban a t függvényt próbáljuk meghívni, pedig a t nem függvény, hanem tömb. A fentihez hasonlóan t(i) helyett t[i] kell.

1
2
3
felszin.c:31:12: warning: implicit declaration of function ‘terulet’ [-Wimplicit-function-declaration]
   31 |     t[0] = terulet(P[1], P[2], P[3]);
      |            ^~~~~~~
A 31. sorban használjuk a terulet függvényt, ami a get_pont-hoz hasonlóan még nincs deklarálva. Javítás a fent leírtakhoz hasonlóan.

1
2
3
felszin.c:31:20: error: ‘P’ undeclared (first use in this function)
   31 |     t[0] = terulet(P[1], P[2], P[3]);
      |                    ^
A gcc szerint nincs deklarálva a P azonosító. Ez a hiba is a struktúra le nem zárásából adódik, a 15-17 sorbeli hiányzó ; beszúrásával megjavul.

1
2
3
felszin.c:36:9: warning: this ‘if’ clause does not guard... [-Wmisleading-indentation]
   36 |         if (t[i] = 0.0)
      |         ^~
A gcc a 36. sorban azt javasolja, hogy ha az if-ben értéket adunk, azt tegyük külön zárójelbe. De megvizsgálva a helyzetet, sokkal valószínűbb, hogy nem értéket akartunk adni, hanem egyenlőséget akartunk ellenőrizni. Tehát a javítás az = operátor ==-re cserélése.

1
2
3
felszin.c:38:13: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the ‘if’
   38 |             return 1;
      |             ^~~~~~
A gcc arra figyelmeztet, hogy 38. sorban lévő return 1; utasítás ránézésre a 37. sor utasításával együtt a 36. sorban található if belsejében van, de valójában nincs, mivel az if (a { hiánya miatt) csak a 37. sorbeli printf-re vonatkozik. A hiba oka, hogy lemaradt a { a 36. sor végéről, ezt pótoljuk. (A 39. sorban viszont látszik, hogy a lezáró } ott van.)

1
2
3
felszin.c:22:12: warning: unused variable ‘felszin’ [-Wunused-variable]
   22 |     double felszin;
      |            ^~~~~~~
A gcc szerint a 22. sorban deklarált felszin változó nincs használva. Ez az előző hiba, a 36. sorban elmaradt { következménye, mert így a 40. sorban lévő }-t a függvény kezdő {-hez párosítja, így ami ez után van (és a felszin használata ez után van), az szerinte nem tartozik a függvényhez. Az előző hiba javításával ez is eltűnik.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
felszin.c: At top level:
felszin.c:41:5: error: expected identifier or ‘(’ before ‘for’
   41 |     for (i = 0; i < 4; ++i) {
      |     ^~~
felszin.c:41:19: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘<’ token
   41 |     for (i = 0; i < 4; ++i) {
      |                   ^
felszin.c:41:24: error: expected identifier or ‘(’ before ‘++’ token
   41 |     for (i = 0; i < 4; ++i) {
      |                        ^~
felszin.c:44:12: error: expected declaration specifiers or ‘...’ before string constant
   44 |     printf("A test felszíne: A = %.3lf\n", felszin);
      |            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
felszin.c:44:45: error: unknown type name ‘felszin’
   44 |     printf("A test felszíne: A = %.3lf\n", felszin);
      |                                             ^~~~~~~
felszin.c:45:5: error: expected identifier or ‘(’ before ‘return’
   45 |     return 0;
      |     ^~~~~~
felszin.c:46:1: error: expected identifier or ‘(’ before ‘}’ token
   46 | }
      | ^
A gcc arra panaszkodik, hogy utasításokat talált függvényen kívül. Ezek is mind javulnak, ha a 36. sor végére beszúrjuk a { karaktert.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
felszin.c:48:1: error: unknown type name ‘pont_t’
   48 | pont_t get_pont(const char *str) {
      | ^~~~~~
felszin.c: In function ‘get_pont’:
felszin.c:49:5: warning: statement with no effect [-Wunused-value]
   49 |     pont_t retval = {0.0, 0.0, 0.0};
      |     ^~~~~~
felszin.c:49:11: error: expected ‘;’ before ‘retval’
   49 |     pont_t retval = {0.0, 0.0, 0.0};
      |           ^~~~~~~
      |           ;
felszin.c:50:34: error: ‘retval’ undeclared (first use in this function)
   50 |     sscanf(str, "(%lf,%lf,%lf)", retval.x, retval.y, retval.z);
      |                                  ^~~~~~
Ezek mindegyike a struktúra-definíció körüli hibából fakad, és a 15-17. sori javítással eltűnnek.

1
2
3
4
5
felszin.c: At top level:
felszin.c:53:23: error: expected ‘]’ before ‘P’
   53 | double tavolsag[pont_t P, pont_t Q] {
      |                       ^~
      |                       ]
Az 53. sorban arra figyelmeztet a gcc, hogy tömb deklarációjában a [] jelek között egyetlen érték szerepelhet, és a P már a második lenne, tehát kell elé a ]. De mi látjuk, hogy a gond valójában az, hogy a függvény definíciójában a () zárójelpárt kellene használni a [] helyett. Javítsuk a hibát.

 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
felszin.c:61:16: error: expected declaration specifiers or ‘...’ before ‘pont_t’
   61 | double terulet(pont_t A, pont_t B, pont_t C) [
      |                ^~~~~~
felszin.c:61:26: error: expected declaration specifiers or ‘...’ before ‘pont_t’
   61 | double terulet(pont_t A, pont_t B, pont_t C) [
      |                          ^~~~~~
felszin.c:61:36: error: expected declaration specifiers or ‘...’ before ‘pont_t’
   61 | double terulet(pont_t A, pont_t B, pont_t C) [
      |                                    ^~~~~~
felszin.c:63:5: warning: data definition has no type or storage class
   63 |     a = tavolsag(B, C);
      |     ^
felszin.c:63:5: warning: type defaults to ‘int’ in declaration of ‘a’ [-Wimplicit-int]
felszin.c:63:9: warning: implicit declaration of function ‘tavolsag’ [-Wimplicit-function-declaration]
   63 |     a = tavolsag(B, C);
      |         ^~~~~~~~
felszin.c:63:18: error: ‘B’ undeclared here (not in a function)
   63 |     a = tavolsag(B, C);
      |                  ^
felszin.c:63:21: error: ‘C’ undeclared here (not in a function)
   63 |     a = tavolsag(B, C);
      |                     ^
felszin.c:64:5: warning: data definition has no type or storage class
   64 |     b = tavolsag(A, C);
      |     ^
felszin.c:64:5: warning: type defaults to ‘int’ in declaration of ‘b’ [-Wimplicit-int]
felszin.c:64:18: error: ‘A’ undeclared here (not in a function)
   64 |     b = tavolsag(A, C);
      |                  ^
felszin.c:65:5: warning: data definition has no type or storage class
   65 |     c = tavolsag(A, B);
      |     ^
felszin.c:65:5: warning: type defaults to ‘int’ in declaration of ‘c’ [-Wimplicit-int]
felszin.c:66:5: warning: data definition has no type or storage class
   66 |     s = (a + b + c) / 2.0;
      |     ^
felszin.c:66:5: warning: type defaults to ‘int’ in declaration of ‘s’ [-Wimplicit-int]
felszin.c:66:9: error: initializer element is not constant
   66 |     s = (a + b + c) / 2.0;
      |         ^
felszin.c:67:5: error: expected identifier or ‘(’ before ‘return’
   67 |     return sqrt(s * (s - a) * (s - b) * (s - c));
      |     ^~~~~~
felszin.c:68:1: error: expected identifier or ‘(’ before ‘]’ token
   68 | ]
      | ^
Amikor ennyi hiba van pár sorban, ott már nagyon valószínű, hogy a korábbi hibák miatt a gcc teljesen elvesztette a fonalat. Itt az alap hiba a struktúra-definíció utáni ; hiány, ami miatt a pont_t-t nem tudja hová tenni a fordító. Illetve a tavolsag függvény definíciójának elrontása miatt (előző hiba az 53. sorban) ezt az azonosítót sem ismeri fel korábban deklarált függvényként. A korábbi hibák javításával ezek nagyrészt el fognak tűnni.

1
2
3
4
felszin.c: In function ‘Main’:
felszin.c:40:5: warning: control reaches end of non-void function [-Wreturn-type]
   40 |     }
      |     ^
A gcc szerint a Main függvény úgy érhet véget a 40. sorban, hogy nem hajt végre return utasítást. Ez ugye megint a 36. sorban hiányzó { miatt van, és annak beszúrásával javul.

1
2
3
4
felszin.c: In function ‘get_pont’:
felszin.c:51:1: warning: control reaches end of non-void function [-Wreturn-type]
   51 | }
      | ^
Az 51. sorban egy hasonló hiba viszont jogos: a get_pont függvényben nincs return, pedig kellene. Mondjuk egy return retval;, a függvény végére.

A gcc fordító kimenete a javított verzión

Mint látszik, maradtak még hibák, amiket a korábbiak miatt a gcc "nem látott".

 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
temp.c: In function ‘Main’:
temp.c:46:17: error: invalid operands to binary + (have ‘double’ and ‘double *’)
   46 |         felszin += t{i};
      |                 ^~ ~
      |                    |
      |                    double *
temp.c:46:21: error: expected ‘;’ before ‘{’ token
   46 |         felszin += t{i};
      |                     ^
      |                     ;
temp.c: In function ‘get_pont’:
temp.c:54:21: warning: format ‘%lf’ expects argument of type ‘double *’, but argument 3 has type ‘double’ [-Wformat=]
   54 |     sscanf(str, "(%lf,%lf,%lf)", retval.x, retval.y, retval.z);
      |                   ~~^            ~~~~~~~~
      |                     |                  |
      |                     double *           double
temp.c:54:25: warning: format ‘%lf’ expects argument of type ‘double *’, but argument 4 has type ‘double’ [-Wformat=]
   54 |     sscanf(str, "(%lf,%lf,%lf)", retval.x, retval.y, retval.z);
      |                       ~~^                  ~~~~~~~~
      |                         |                        |
      |                         double *                 double
temp.c:54:29: warning: format ‘%lf’ expects argument of type ‘double *’, but argument 5 has type ‘double’ [-Wformat=]
   54 |     sscanf(str, "(%lf,%lf,%lf)", retval.x, retval.y, retval.z);
      |                           ~~^                        ~~~~~~~~
      |                             |                              |
      |                             double *                       double
temp.c: In function ‘tavolsag’:
temp.c:63:12: warning: implicit declaration of function ‘sqrt’ [-Wimplicit-function-declaration]
   63 |     return sqrt(d.x * d.x + d.y * d.y + d.z * d.z);
      |            ^~~~
temp.c:63:12: warning: incompatible implicit declaration of built-in function ‘sqrt’
temp.c:10:1: note: include ‘<math.h>’ or provide a declaration of ‘sqrt’
    9 | #include <stdio.h>
  +++ |+#include <math.h>
   10 | 
temp.c: At top level:
temp.c:67:5: error: expected expression before ‘double’
   67 |     double a, b, c, s;
      |     ^~~~~~

Megoldás, 2. lépés

A javítások miatt a sorszámok itt eltérhetnek a fentiektől.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
felszin.c: In function ‘Main’:
felszin.c:46:17: error: invalid operands to binary + (have ‘double’ and ‘double *’)
   46 |         felszin += t{i};
      |                 ^~ ~
      |                    |
      |                    double *
felszin.c:46:21: error: expected ‘;’ before ‘{’ token
   46 |         felszin += t{i};
      |                     ^
      |                     ;
Ebben a sorban több hibát is jelez a fordító. Mindkettő arra vezethető vissza, hogy a ` tömböt[]helyett{}`-vel próbáljuk meg indexelni. Javítsuk!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
felszin.c: In function ‘get_pont’:
felszin.c:54:21: warning: format ‘%lf’ expects argument of type ‘double *’, but argument 3 has type ‘double’ [-Wformat=]
   54 |     sscanf(str, "(%lf,%lf,%lf)", retval.x, retval.y, retval.z);
      |                   ~~^            ~~~~~~~~
      |                     |                  |
      |                     double *           double
felszin.c:54:25: warning: format ‘%lf’ expects argument of type ‘double *’, but argument 4 has type ‘double’ [-Wformat=]
   54 |     sscanf(str, "(%lf,%lf,%lf)", retval.x, retval.y, retval.z);
      |                       ~~^                  ~~~~~~~~
      |                         |                        |
      |                         double *                 double
felszin.c:54:29: warning: format ‘%lf’ expects argument of type ‘double *’, but argument 5 has type ‘double’ [-Wformat=]
   54 |     sscanf(str, "(%lf,%lf,%lf)", retval.x, retval.y, retval.z);
      |                           ~~^                        ~~~~~~~~
      |                             |                              |
      |                             double *                       double
A sor mindhárom hibájának ugyanaz az oka: a scanf nem double típusú változó értéket, hanem double* típusú változó címet vár. Emlékezzünk: ha scanf, akkor a változó neve elé kell egy &. Három helyen is.

1
2
3
4
5
6
7
8
9
felszin.c: In function ‘tavolsag’:
felszin.c:63:12: warning: implicit declaration of function ‘sqrt’ [-Wimplicit-function-declaration]
   63 |     return sqrt(d.x * d.x + d.y * d.y + d.z * d.z);
      |            ^~~~
felszin.c:63:12: warning: incompatible implicit declaration of built-in function ‘sqrt’
felszin.c:10:1: note: include ‘<math.h>’ or provide a declaration of ‘sqrt’
    9 | #include <stdio.h>
  +++ |+#include <math.h>
   10 | 
Ez szép, itt a gcc pontosan megmondja, mit kellene csinálni. Tegyük meg!

1
2
3
4
felszin.c: At top level:
felszin.c:67:5: error: expected expression before ‘double’
   67 |     double a, b, c, s;
      |     ^~~~~~
Az At top level mutatja, hogy a gcc szerint nem függvényben vagyunk. Azért gondolja, mert {} helyett []-t használtunk a függvénytörzs "bekeretezésére". Javítsuk!

A gcc fordító kimenete az újabb javítás után

Pedig már azt hittük, minden szép és jó...

1
2
3
4
5
6
7
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/Scrt1.o: in function `_start':
(.text+0x24): undefined reference to `main'
/usr/bin/ld: /tmp/ccfBwX69.o: in function `tavolsag':
temp.c:(.text+0x3cb): undefined reference to `sqrt'
/usr/bin/ld: /tmp/ccfBwX69.o: in function `terulet':
temp.c:(.text+0x499): undefined reference to `sqrt'
collect2: error: ld returned 1 exit status

Megoldás, 3. lépés

1
2
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/Scrt1.o: in function `_start':
(.text+0x24): undefined reference to `main'
Ez a hiba azt jelzi, hogy nincs meg a main függvény, így a linker nem tud programot összeállítani. Van ugyan egy Main függvényünk, de a C érzékeny a kis- és nagybetűkre, és ugye Nemecsek Ernő óta tudjuk, hogy a Main az nem main. Szóval, a sorráskódban javítsuk ki az M-et m-re.

1
2
3
4
/usr/bin/ld: /tmp/ccfBwX69.o: in function `tavolsag':
temp.c:(.text+0x3cb): undefined reference to `sqrt'
/usr/bin/ld: /tmp/ccfBwX69.o: in function `terulet':
temp.c:(.text+0x499): undefined reference to `sqrt'
Hasonló hiba, csak ezúttal az sqrt függvény hiányzik (amit a tavolsag és terulet függvényeink használnak). Ez viszont már nem a forráskódban javítandó. Egyszerűen adjuk meg a gcc-nek a -lm kapcsolót, hogy ott keresse az sqrt függvényt ahol az van: a matematikai függvénykönyvtárban.

1
collect2: error: ld returned 1 exit status
Ebből a sorból látszik, hogy ezek már nem fordítási, hanem szerkesztési (linker) hibák. Ami persze nem jelenti azt, hogy nem a forráskódban van a probléma.

Megoldás egyben (videó)

felszin.c

Ez alapján javítsuk ki magunktól a következő feladat szintaktikai hibáit!

Feladat (f0264)

Feladat:

A delta.c program feladata lenne óra:perc:másodperc.milisec alakban megadott egymást követő időpontok közötti eltéréseket számolni. A program algoritmusa alapvetően helyes, de a megvalósításában, kódolásában több hiba van. Javítsd ki ezeket a fordító által jelzett hibák és figyelmeztetések alapján!

 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
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 *
 * Programozás Alapjai
 */

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

struct ido (
    int hrs;
    int min;
    int sec;
    int ms;
)

main() {
    struct ido prev, curr;
    beolvas(prev);
    kiir(' ', prev);
    while(beolvas(&curr)) {
        prev = kulonbseg(prev, curr);
        kiir('+', prev);
        prev = curr;
    }
}

bool beolvas{struct ido *t} [
    return (scanf("%d:%d:%d.%d", t->hrs, t->min, t->sec, t->ms) = 4);
]

void kiir[char prefix, struct ido t] (
    if (t.hrs > 0) {
        printf("%c%2d:%02d:%02d.%03d\n", prefix, t.hrs, t.min, t.sec, t.ms);
        return 0;
    }
    if (t.min > 0) {
        printf("%c%5d:%02d.%03d\n", prefix, t.min, t.sec, t.ms);
        return 0;
    }
    printf("%c%8d.%03d\n", prefix, t.sec, t.ms);
)

struct ido kulonbseg(struct ido start, struct ido stop) {
    start.ms  = {[(stop.hrs-start.hrs)*60+(stop.min-start.min)]*60+(stop.sec-start.sec)}*1000+(stop.ms-start.ms);
    start.hrs = start.ms / 3600000;
    start.ms %= 3600000;
    start.min = start.ms /   60000;
    start.ms %=   60000;
    start.sec = start.ms /    1000;
    start.ms %=    1000;
    return start;
}
felszin.c

A gcc fordító kimenete
 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
delta.c:13:5: error: expected identifier or ‘(’ before ‘int’
   13 |     int hrs;
      |     ^~~
delta.c:30:1: error: unknown type name ‘bool’
   30 | bool beolvas{struct ido *t} [
      | ^~~~
delta.c:30:13: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘{’ token
   30 | bool beolvas{struct ido *t} [
      |             ^
delta.c:30:29: error: expected identifier or ‘(’ before ‘[’ token
   30 | bool beolvas{struct ido *t} [
      |                             ^
delta.c:32:1: error: expected identifier or ‘(’ before ‘]’ token
   32 | ]
      | ^
delta.c:39:5: error: expected identifier or ‘(’ before ‘if’
   39 |     if (t.min > 0) {
      |     ^~
delta.c:43:12: error: expected declaration specifiers or ‘...’ before string constant
   43 |     printf("%c%8d.%03d\n", prefix, t.sec, t.ms);
      |            ^~~~~~~~~~~~~~
delta.c:43:28: error: unknown type name ‘prefix’
   43 |     printf("%c%8d.%03d\n", prefix, t.sec, t.ms);
      |                            ^~~~~~
delta.c:43:36: error: unknown type name ‘t’
   43 |     printf("%c%8d.%03d\n", prefix, t.sec, t.ms);
      |                                    ^
delta.c:43:43: error: unknown type name ‘t’
   43 |     printf("%c%8d.%03d\n", prefix, t.sec, t.ms);
      |                                           ^
delta.c:44:1: error: expected identifier or ‘(’ before ‘)’ token
   44 | )
      | ^
Megoldás (videó)

str_rev

Egy kis programozás fun otthonra

Írjunk egy bombakereső játékot!

A játékot a számítógép ellen játszuk. A számítógép induláskor feltölt egy \(10 \times 10\)-es tömböt random \(0\)-kal és \(1\)-esekkel, úgy, hogy pl. 10 darab \(1\)-es legyen a táblán, a többi mind \(0\). Az \(1\)-esek szimbolizálják a bombákat. A játék kezdésekor a játékos úgyszint kitölt egy ugyanekkora táblát a saját elképzelése szerint, úgy, hogy megadja, hogy melyik pozícióra kerüljenek a bombái: \([i,j]\). Miután a random generátor feltöltötte az ellenfél tábláját, illetve a játékos is feltöltötte a sajátját, megkezdődhet a játék. Ezután felváltva találgathat a két fél, hogy szerintük hol lehet bomba az ellenfél tábláján. A számítógép tippelhet véletlen módon is, de eljátszhatunk azzal is, hogy minél jobb ellenfelet írjunk a játékosunknak. Az nyer, aki előbb megtalálja az ellenfél összes bombáját. A játék végén írjuk ki, hogy ki nyerte a játszmát.

Egy kis segítség

Nyugodtan töltsük fel mindkét táblát kezdetben \(0\)-kal, majd ahova érkezik \(1\)-es, ott csak írjuk felül. A számítógép táblájának feltöltésére egy lehetséges megoldás, hogy két számot generáltatunk: az egyik lesz az oszlop indexe, a másik pedig a sornak az indexe. Így jön ki pl., hogy hova kerüljön \(1\)-es. Bevezethetünk két segéd változót, amely azt figyeli, hogy ki hány bombát talált eddig. Ha valamelyik változó értéke eléri a 10-et, meg van az összes bomba, a játék véget ér.

Ügyeljünk rá, hogy ...

Kétszer ugyanoda tippelve, ne szerezzen egyik fél sem mégegyszer pontot. Legyegyszerűbb, ha egy találat esetén egyből felülírjuk \(0\)-val az adott pozíciót az adott tömbben. Ellenőrizzük, a játékos által megadott pozíciókra, hogy a \(10 \times 10\)-es tömbön belül vannak-e.

A kódot kommentezzük is úgy, hogy ha más valaki olvassa az is megértse! (Melyik változó / elágazás / ciklus / függvény /.. mire való.)

A feladat megoldását/próbálgatását erősen ajánlom mindenkinek. Sok mindenre rá lehet jönni ilyenkor. Próbáljunk meg a körülményekhez mérten felhasználóbarát programot készíteni!

Feladatok

Feladat (f0183)

Problémafelvetés:

Írj egy programot ami három háromdimenziós koordináta-hármasból kiszámítja egy térbeli háromszög kerületét és területét!

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

A megfelelő helyeken használj struct adattípusokat.

Feladat (f0184)

Problémafelvetés:

Írj egy programot ami négy háromdimenziós koordináta-hármasból kiszámítja egy pontok által határolt térrész (egyfajta szabálytalan "tetraéder", oldalaikkal és csúcsaikkal érintkező négy háromszög által határolt test, melynek csúcsai a megadott pontok) felszínét.

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

A megfelelő helyeken használj struct adattípusokat.

Lehetséges megoldás (m0184.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
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 *
 * Programozás Alapjai
 */

#include <stdio.h>
#include <math.h>

struct pont {
    double x;
    double y;
    double z;
};

typedef struct pont pont_t;

pont_t get_pont();
double tavolsag(pont_t P, pont_t Q);
double terulet(pont_t A, pont_t B, pont_t C);

int main(int argc, char *argv[]) {
    pont_t P[4];
    double t[4];
    double felszin = 0.0;
    for (int i = 0; i < 4; ++i) {
        P[i] = get_pont();
        t[i] = 0.0;
    }
    t[0] = terulet(P[1], P[2], P[3]);
    t[1] = terulet(P[0], P[2], P[3]);
    t[2] = terulet(P[0], P[1], P[3]);
    t[3] = terulet(P[0], P[1], P[2]);
    for (int i = 0; i < 4; ++i) {
        if (t[i] == 0.0) {
            printf("A megadott pontok egy síkba esnek.\n");
            return 1;
        }
    }
    for (int i = 0; i < 4; ++i) {
        felszin += t[i];
    }
    printf("A test felszíne: A = %.3lf\n", felszin);
    return 0;
}

pont_t get_pont() {
    pont_t retval = {0.0, 0.0, 0.0};
    scanf("%*[ \t\n]"); // Ha van whitespace karakter, azt nyeljük le
    scanf("(%lf,%lf,%lf)", &retval.x, &retval.y, &retval.z);
    return retval;
}

double tavolsag(pont_t P, pont_t Q) {
    pont_t d;
    d.x = P.x - Q.x;
    d.y = P.y - Q.y;
    d.z = P.z - Q.z;
    return sqrt(d.x * d.x + d.y * d.y + d.z * d.z);
}

double terulet(pont_t A, pont_t B, pont_t C) {
    double a, b, c, s;
    a = tavolsag(B, C);
    b = tavolsag(A, C);
    c = tavolsag(A, B);
    s = (a + b + c) / 2.0;
    return sqrt(s * (s - a) * (s - b) * (s - c));
}
Feladat (f0185)

Problémafelvetés:

Írj egy programot ami komplex számokkal képes sorban alapműveleteket elvégezni, zárójelezés és precedencia figyelembevétele nélkül. Vagyis például a 3+2*5 eredménye nem 3+(2*5)=13, hanem (3+2)*5=25.

Specifikáció:

A program inputja több sorból áll. A páratlan sorokban egy-egy komplex szám, a páros sorokban egy-egy műveleti jel szerepel, a sorvégeken kívül egyéb whitespace karakterek nélkül. Egy komplex szám A+Bi vagy A-Bi alakban lesz megadva, ahol A egy előjellel vagy anélkül megadott valós szám, B pedig egy előjel nélkül megadott valós szám. A szám után közvetlenül egy 'i' karakter áll. A műveleti jel '+', '-', '*', '/' vagy '=' lehet. Az '=' a bemenet végét jelenti. A program kimenete egy komplex szám, a műveletsorozat eredménye, a fent leírt alakban. A műveletsorozatot fentről lefelé hajtjuk végre, és minden művelet azonos prioritásúnak számít.

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

Használjunk struct adattípust a komplex számok tárolására, illetve külön függvényeket az egyes műveletekre, egy komplex szám beolvasására illetve kiírására is.

Feladat (f0187)

Feladat:

Fordítsd le és futtasd a rekordok.c programot. Figyeld meg és magyarázd el, mi a különbség a struct és a union között! Most alakítsd át a programot úgy, hogy előbb hajtsd végre az összes értékadást, és csak utána a kiíratásokat. Most mit tapasztalsz? Miért? Minden egyes kiíratáskor írasd ki az éppen kiírt mező kezdőcímét is. Mit tapasztalsz?

 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
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 *
 * Programozás Alapjai
 */

#include <stdio.h>

typedef struct {int i; double d; char c; float f;} st_t;
typedef union {int i; double d; char c; float f;} un_t;

int main() {
    st_t s;
    un_t u;
    s.i = u.i = 12345;
    printf("s.i: %d  u.i: %d\n", s.i, u.i);
    s.d = u.d = 3.141593;
    printf("s.d: %lf  u.d: %lf\n", s.d, u.d);
    s.c = u.c = 'A';
    printf("s.c: %c  u.c: %c\n", s.c, u.c);
    s.f = u.f = 2.718281;
    printf("s.f: %f  u.f: %f\n", s.f, u.f);
    return 0;
}
rekordok.c

Feladat (f0226)

Problémafelvetés:

Írj egy programot, ami kiszámolja egy konvex sokszög súlypontját.

Specifikáció:

A program inputja egy n egész szám, majd n darab valós számpár (x,y) alakban. Minden számpár a konvex sokszög egy-egy pontjának koordinátája, valamely körüljárási irányszerinti sorrendben. A program outputja a sokszög súlypontjának koordináta-párja. Ezt az egy és kétcsúcsú elfajuló síkidomokra is ki kell tudni számolni.

Algoritmustervezés:

A sokszög konvexségét nem kell ellenőrízni. Egy sokszög súlypontját úgy kaphatjuk meg, hogy nem átfedő háromszögekre bontjuk, és kiszámoljuk ezen háromszögek súlypontjainak a háromszögek területével (a területük sokszögön belüli arányával) súlyozott átlagát. Azaz ha például egy 3 egység területű négyszöget egy 1 és egy 2 egység területű háromszögre bontunk fel, akkor az első háromszög súlypontját 1/3, a másodikét 2/3 súllyal számítjuk bele az átlagba.

További gyakorló feladatok

Feladat (f0093)

Problémafelvetés:

Írj egy programot, ami bekéri egy világos és egy sötét bábu rangját és sakktáblán elfoglalt pozícióját, majd kiírja, hogy a világos bábu üti, feltételesen ütheti, vagy biztosan nem üti a másik bábut, esetleg az állás (a helyes koordináták ellenére) nem lehetséges. Helytelen koordinátákra (nem sakktábla mező) a program ne írjon ki semmit, helyes koordinátákra pedig az "Üti.", "Nem üti.", "Feltételekkel ütheti." és "Nem lehetséges." szöveg valamelyikét.

Feladat (f0192)

Problémafelvetés:

Írj egy programot, ami bekéri egy torta piskótájának sugarát és magasságát, majd kiszámolja a torta 1cm vastag bevonásához szükséges krém térfogatát 5%-os ráhagyással dolgozva. (A torta alját nem kell bekrémezni, csak az oldalát és a tetejét.)

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. Az adatok tárolására használj összetett adatszerkezetet, ha van értelme.

Feladat (f0194)

Problémafelvetés:

Írj egy programot, ami megmondja, hogy egy hallgató teljesítette-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 a hallgató nevét, pontjait tároljuk. A program tároljon külön minden részpontszámot és a hiányzásokat is.

Feladat (f0206)

Problémafelvetés:

Adott két körszerű test a kétdimenziós síkon. A testek helyzetét a középpontjaikkal azonosítjuk. Kezdetben a felhasználó mindkét testhez megadja:

  1. a középpontját \(x,y\) koordinátákkal,
  2. sugarát,
  3. kezdősebességét és annak irányát, valamint
  4. a test tömegét.

Megad továbbá egy \(\Delta t\) és egy \(T\) időintervallumot. A feladat a két test kétdimenziós fizikai mozgásának szimulálása a \(0..T\) időintervallumban \(\Delta t\) időbeosztással. Ez azt jelenti, hogy a kezdő időpontban kiszámoljuk a testekre ható erőket, majd a test mozgását a következő \(\Delta t\) időintervallumban úgy közelítjük, mintha a testre ebben az időintervallumban nem hatna egyéb erő. Ezután újra kiszámítjuk az erőket, majd újra közelítjük a mozgást, stb. A mozgás szimulálása a \(T\) időpontig vagy a két test "ütközéséig" tart. A pontosság kedvéért a két test helyzetét (\(x,y\) koordinátáit) úgy írassuk ki, mintha az origo, azaz a koordinátarendszer középpontja a két test közös tömegközéppontja lenne, azaz a kiszámított koordinátaértékeket ezek szerint korrigáljuk minden lépésben.

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. Az adatok tárolására használj összetett adatszerkezetet, ha van értelme.

Kapcsolódó linkek


Utolsó frissítés: 2021-09-01 11:11:07