Funcții și prototipuri în C

Funcții C89

Cum definești și declari funcții în C: tipul rezultatului, parametrii, prototipul și transmiterea argumentelor prin valoare.

Sintaxă
tip_returnat nume(tip1 param1, tip2 param2);   // prototip (declarație)
tip_returnat nume(tip1 param1, tip2 param2) {  // definiție
    ...
    return valoare;
}
void nume(void);   // fără parametri, fără rezultat

Ce face Funcții și prototipuri

O funcție are un tip de rezultat, un nume, o listă de parametri și un corp. return trimite rezultatul înapoi; o funcție void nu returnează nimic. Compilatorul trebuie să vadă declarația unei funcții înainte de primul apel: fie definiția completă, fie un prototip (doar antetul, terminat cu punct și virgulă), de obicei pus într-un fișier .h.

În C, argumentele se transmit prin valoare: funcția primește o copie. Dacă modifici parametrul în funcție, variabila din apel rămâne neschimbată. Ca să modifici variabila celui care apelează, trimiți adresa ei (un pointer).

Din C23, int f() înseamnă la fel ca int f(void): funcție fără parametri. În standardele mai vechi, parantezele goale însemnau „parametri nespecificați”. Tot din C23 (și deja din C99 cu avertisment), apelarea unei funcții nedeclarate este eroare.

Parametri

Parametru Descriere
tip_returnat Tipul valorii returnate (int, double, char *…) sau void.
parametri Lista de parametri, fiecare cu tipul lui; void sau nimic pentru „fără parametri”.

Exemple

Exemplul 1: Prototip înainte de main, definiție după

Prototipul de sus permite apelul din main, chiar dacă definiția vine mai jos.

#include <stdio.h>

double pret_cu_tva(double pret, double cota);   // prototip

int main(void) {
    printf("Laptop: %.2f lei\n", pret_cu_tva(3000, 0.21));
    printf("Carte:  %.2f lei\n", pret_cu_tva(50, 0.11));
    return 0;
}

double pret_cu_tva(double pret, double cota) {
    return pret * (1 + cota);
}

Rezultat

Laptop: 3630.00 lei
Carte:  55.50 lei

Exemplul 2: Transmitere prin valoare

dubleaza modifică doar copia; x din main rămâne 5. Versiunea cu pointer modifică variabila originală.

#include <stdio.h>

void dubleaza(int n) {
    n *= 2;
    printf("în funcție: %d\n", n);
}

void dubleaza_prin_pointer(int *n) {
    *n *= 2;
}

int main(void) {
    int x = 5;
    dubleaza(x);
    printf("după dubleaza: %d\n", x);
    dubleaza_prin_pointer(&x);
    printf("după dubleaza_prin_pointer: %d\n", x);
    return 0;
}

Rezultat

în funcție: 10
după dubleaza: 5
după dubleaza_prin_pointer: 10

Exemplul 3: Apel fără declarație: eroare în C23

Compilatorul nu știe ce este suma în momentul apelului. Soluția este un prototip deasupra lui main sau mutarea definiției mai sus.

#include <stdio.h>

int main(void) {
    int total = suma(2, 3);
    printf("%d\n", total);
    return 0;
}

int suma(int a, int b) { return a + b; }

Eroare de compilare (clang)

exemplu.c:4:17: error: use of undeclared identifier 'suma'
    4 |     int total = suma(2, 3);
      |                 ^~~~
1 error generated.

Greșeli frecvente

  • Uiți return într-o funcție care nu este void. Dacă apelantul folosește rezultatul, comportamentul este nedefinit (-Wall avertizează).
  • Returnezi adresa unei variabile locale (return &x;). Variabila dispare la ieșirea din funcție; folosește static, memorie alocată sau un parametru de ieșire.
  • Aștepți ca funcția să modifice tabloul fără să îl primească, sau o variabilă simplă trimisă prin valoare. Pentru modificare trimite un pointer.

Comenzi înrudite

  • main: Funcția cu care începe orice program C: sistemul o apelează la pornire, iar valoarea returnată spune dacă programul a reușit.
  • Recursivitate: O funcție care se apelează pe ea însăși, cu o problemă mai mică la fiecare pas, până la un caz de bază.
  • Pointeri (& și *): O variabilă care păstrează adresa altei variabile; cu * citești sau modifici valoarea de la acea adresă.
  • Pointeri la funcții: O variabilă care ține adresa unei funcții: o poți schimba, pune în tablouri sau trimite altor funcții (callback).
  • Programe din mai multe fișiere (.c și .h): Cum împarți un program C în fișiere sursă (.c) și antete (.h), cum le compilezi separat și cum le legi într-un executabil.
  • static: Într-o funcție, variabila își păstrează valoarea între apeluri; la nivel de fișier, ascunde funcția sau variabila de celelalte fișiere.