Utilizarea și programarea calculatoarelor 2 - Laborator 3
Erori de compilare și interpretarea lor
În practica programării, e absolut necesar să ne obișnuim să înțelegem și
să corectăm singuri erorile de compilare. E bine de asemenea, pe măsură
ce realizăm programul pas cu pas, și am ajuns la o fază intermediară
consistentă, să îl compilăm. Adeseori, mai ales pentru programatorii aflați
la început, erorile de compilare ne pot semnala și probleme de logică a
programului (folosirea incorectă a tipurilor, sau a unor funcții standard),
iar descoperirea lor cât mai timpurie evită pierderea timpului.
Ca mai toate compilatoarele, gcc încearcă să localizeze cât mai
precis cauza erorii și prefixează fiecare mesaj cu linia în care apare
eroarea în fișier:
/home/upc/test.c:16 mesaj de eroare propriu-zis
Uneori eroarea se poate afla de fapt puțin înaintea punctului în care
ea a fost detectată; în orice caz e bine să ne obișnuim să corectăm erorile
pe rând, în ordinea apariției, pentru că o eroare gravă poate determina la
rândul ei semnalarea a multe alte erori (și invers, înlăturarea ei poate
reduce din numărul mesajelor ulterioare).
Mesaje de eroare
Câteva din cele mai întâlnite mesaje, cu cauzele lor posibile și exemple de
cod în care apar, sunt indicate mai jos:
- syntax error at end of input
Mesajul nu e foarte specific; compilatorul a ajuns la sfârșit de fișier
și a găsit că acesta nu se termină în mod corespunzător. Cel mai adesea,
asta înseamnă lipsa unei acolade, sau dimpotrivă, cod care de fapt e scris
în afara unei funcții. Putem indenta programul corect, verifica potrivirea
acoladelor, și evita această eroare prin împerecherea lor de la bun început
și respectarea regulilor de indentare.
- parse error before 'fp'
file fp;
Tipul file este necunoscut; ar trebuie să fie FILE
(de fapt, FILE *, și trebuie inclus stdio.h). Eroarea
e raportată puțin mai târziu, la încercarea declarării lui fp.
- parse error before începutul unei instrucțiuni
Cel mai adesea, instrucțiunea dinainte nu a fost încheiată cu ;
- `x' undeclared (first use in this function)
Cea mai simplă eroare: folosirea unei variabile nedeclarate.
Atenție însă la alegerea numelor de variabile: , pentru a
- break statement not within loop or switch
Instrucțiunea break poate fi folosită numai în interiorul
unui ciclu (do, for sau while) sau în instrucțiunea
switch. Frecvent, eroarea apare când s-au uitat acoladele în jurul
unui șir de instrucțiuni intenționat ca instrucțiune compusă; în acest caz,
doar prima instrucțiune face parte din ciclu, restul sunt în afara lui.
- invalid lvalue in assignment
if (!fp = fopen("fisier.txt", "r")) /* ... */
Atribuirea având precedentă scăzută, se evaluează întâi !fp, și
se interpretează expresia ca o incercare de a-i atribui o valoare, fapt eronat
pentru că !fp este o expresie oarecare, nu este o variabilă și
nu poate fi atribuită (la fel cum nu poate fi atribuit x + 3 = y).
Mesaje de avertisment
Deși programul compilează cu succes, aceste mesaje trebuie urmărite cu
atenție. Foarte des, ele semnalează erori logice în program (atribuiri
în loc de comparații, atribuiri sau parametri cu tip greșit), care sunt
dificil de detectat altfel la rulare. E important să ne obișnuim să scriem
programe care NU generează mesaje de avertisment.
warning: passing arg 1 of `strlen' makes pointer from integer without a cast
l = strlen(p[i]); /* cu char *p; int i, l; */
S-a transmis un întreg (caracter) pe post de parametru acolo unde ar fi
fost nevoie de un pointer. Pentru a corecta eroarea, trebuie verificat în
pagina de manual ce fel de parametri ia funcția respectivă și ce reprezintă ei.
În cazul în care programul e compilat cu opțiunea -Wall și nu a fost
inclus fișierul string.h cu declarația lui strlen se va
raporta în plus:
warning: implicit declaration of function `strlen'
Folosirea funcțiilor fără a fi declarate nu e recomandabilă pentru că
nu dă compilatorului posibilitatea să verifice corespondența tipurilor dintre
parametrii formali (din declarație) și cei actuali (din apel), ceea ce este
o frecventă sursă de erori. Pentru fiecare funcție standard folosită trebuie
deci inclus fișierul antet corespunzător.
warning: suggest parentheses around assignment used as truth value
if (c = getchar() != EOF) /* ... */
Compilatorul avertizează că o atribuire e folosită pe post de valoare
de adevăr într-un test. Aici, != are precedență mai mare, deci
valoarea returnată de getchar() e comparată cu EOF, și
rezultatul (o valoare de adevăr, întregul 1 sau 0) e atribuit lui c.
Efectul dorit, de atribuire a lui c și apoi testul de
sfârșit de fișier se obține cu paranteze: if ((c = getchar()) != EOF)
sau cu riscuri mai mici de eroare, simplificând expresia și folosind
instrucțiuni separate: c = getchar(); if (c != EOF) /* ... */.
Același mesaj e generat și în cazul unei atribuiri simple folosită
ca și test: if (a = b) care uneori e în intenția programatorului
(ca în while (*s1++ = *s2++); dar adesea e o eroare de neatenție.
warning: assignment makes pointer from integer without a cast
FILE *fp; if (fp = fopen("fisier.txt", "r") == NULL) exit(1);
Aici, compilatorul a detectat o eroare de logică similară celei de mai sus
verificând corespondența tipurilor în expresie: valoarea întreagă rezultată
din comparația între rezultatul lui fopen și NULL e
atribuită unui pointer la fișier. Se dorea de fapt întâi atribuirea lui
fp și apoi compararea: if ((fp = fopen(...)) == NULL).
Mesajul apare însă și în multe alte sițuații în care se confuzionează
întregi cu pointeri, de exemplu:
char s[20]; char *p; int i; /* câutăm în s, apoi memorăm pozitia */ p = s[i]
Aici, s[i] este un caracter (deci un întreg) și nu poate fi
atribuit pointerului p. Ar trebui p = &s[i].
warning: assignment from incompatible pointer type
FILE *fp = argv[1];
Într-o atribuire, atât partea stângă cât și partea dreaptă sunt pointeri,
dar de tip diferit, ceea ce de regulă denotă o eroare logică în program.
În exemplul indicat, se face o atribuire de la un argument de pe linia de
comandă (care este un șir de caractere) la un fișier, ceea ce este incorect
(fp trebuia atribuit cu rezultatul deschiderii (fopen)
fișierului cu numele dat). O variantă a mesajului este și
warning: passing arg 1 of `fprintf' from incompatible pointer type
fprintf("numele programului este %s", argv[0]);
Unei funcții care ia ca parametru un pointer (în acest caz fprintf)
i se transmite un pointer de tip eronat (primul parametru trebuie să fie
FILE * în acest caz).
warning: return type of `main' is not `int'
Standardul C prevede că tipul lui main> este int pentru
a putea returna un cod sistemului de operare (semnalând terminarea cu succes
sau eroare a programului). Dacă nu există intenția de a interpreta modul
de terminare a programului, acest mesaj se poate ignora. Totuși, e mai
frumos să se respecte convenția și să se returneze 0 (succes).
warning: control reaches end of non-void function
O funcție care nu e void trebuie să returneze o valoare, pe
orice cale de execuție posibilă prin corpul funcției. Dacă compilatorul
detectează că se poate atinge acolada de sfârșit } a funcției
pentru că nu s-a executat explicit un return cu o valoare, el
avertizează că în acest caz valoarea returnată de funcție e nedeterminată.
warning: too few arguments for format
int x, y; printf("%d "%d", x);
În cazul în care se compilează cu opțiunea -Wall, compilatorul
face verificări și pentru corespondența argumentelor la funcțiile de tip
printf și scanf cu format și număr variabil de argumente
(altfel, aceste verificări nu se fac!). Aici, compilatorul a detectat că
formatul specifică tipărirea a doi întregi, dar s-a dat ca argument doar unul.
(La rulare, programul ar tipări ca a doua valoare conținutul care se află
întâmplător pe stivă în acel loc).
warning: format argument is not a pointer (arg 2)
int x; scanf("%d", x);
Tot cu opțiunea -Wall, compilatorul avertizează că argumentul
2 care conform formatului ar trebui să fie adresa unde să fie citit
întregul, nu este o adresă (ci o valoare de întreg). Corect ar fi fost
scanf("%d", &x);
Lista de mai sus nu este nici pe departe exhaustivă. La apariția unui mesaj
necunoscut, trebuie să îl citim cu atenție, (mesajele sunt concise,
dar exprimă de regulă precis eroarea), și apoi să urmărim în detaliu
locul indicat în program, regândind ce am dorit să exprimăm cu instrucțiunea
respectivă.
Marius Minea
Last modified: Sun Mar 21 18:52:23 EET 2004