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:

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