Compilarea cu gcc și clang în C

Compilare și build GCC 14 / Clang 18 (pentru -std=c23)

Cum transformi codul C într-un program executabil cu gcc sau clang și ce fac opțiunile folosite cel mai des.

Sintaxă
clang program.c -o program
gcc -std=c23 -Wall -Wextra -Wpedantic -O2 program.c -o program
clang -c fisier.c            # doar compilare, produce fisier.o
clang -E fisier.c            # doar preprocesare
clang -g -fsanitize=address,undefined program.c -o program

Ce face Compilarea cu gcc și clang

Un compilator C (gcc pe Linux, clang pe macOS, ambele pe Windows prin MSYS2 sau WSL) face patru pași: preprocesare (#include, #define), compilare în limbaj de asamblare, asamblare într-un fișier obiect (.o) și legare (linking) cu bibliotecile, rezultând executabilul. O singură comandă, clang program.c -o program, le face pe toate; -E, -S și -c te opresc după primul, al doilea sau al treilea pas.

Opțiuni de bază: -o nume (numele executabilului; fără ea se numește a.out), -std=c23 (standardul; fără ea, Clang 21 folosește C17 cu extensii GNU, iar GCC 15 folosește C23 cu extensii GNU), -Wall -Wextra (avertismente utile), -Wpedantic (avertismente pentru extensii nestandard), -Werror (avertismentele devin erori), -O0/-O2 (fără optimizare / optimizare obișnuită), -g (informații pentru debugger), -DNUME (definește o macro), -I dir (director pentru antete), -lm (leagă biblioteca matematică pe Linux).

Pentru căutarea greșelilor de memorie, -fsanitize=address (AddressSanitizer) și -fsanitize=undefined (UBSan) adaugă verificări la rulare: programul se oprește cu un raport exact când citește în afara unui tablou, folosește memorie eliberată sau face o depășire de int.

Parametri

Parametru Descriere
-o program Numele fișierului executabil.
-std=c23 Standardul limbajului: c89, c99, c11, c17, c23 (sau gnu23 cu extensii).
-Wall -Wextra -Wpedantic Activează avertismentele recomandate.
-Werror Orice avertisment oprește compilarea.
-O0, -O2, -O3, -Os Nivelul de optimizare.
-g Include informații pentru depanare (gdb, lldb).
-c / -S / -E Se oprește după compilare (.o), după generarea codului de asamblare (.s) sau după preprocesare.
-DNUME=val, -I dir, -l bib Definește o macro, adaugă un director de antete, leagă o bibliotecă (-lm).
-fsanitize=address,undefined Verificări la rulare pentru erori de memorie și comportament nedefinit.

Exemple

Exemplul 1: Compilare și rulare

Comanda compilează salut.c cu standardul C23 și avertismentele activate, apoi ./salut pornește programul. Dacă nu sunt erori, compilatorul nu afișează nimic.

// salut.c
#include <stdio.h>

int main(void) {
    printf("Salut din Iași!\n");
    return 0;
}

// în terminal:
// clang -std=c23 -Wall -Wextra -Wpedantic -O2 salut.c -o salut
// ./salut

Ieșire în terminal

Salut din Iași!

Exemplul 2: Avertisment, apoi eroare cu -Werror

Cu -Wall -Wextra, variabila nefolosită produce un avertisment, dar programul se construiește. Cu -Werror, același avertisment devine eroare și executabilul nu mai este creat.

// nota.c
#include <stdio.h>

int main(void) {
    int nota = 9;
    int neutilizat;
    printf("Nota Anei: %d\n", nota);
    return 0;
}

// în terminal:
// clang -std=c23 -Wall -Wextra nota.c -o nota
// clang -std=c23 -Wall -Wextra -Werror nota.c -o nota

Ieșire în terminal

nota.c:6:9: warning: unused variable 'neutilizat' [-Wunused-variable]
    6 |     int neutilizat;
      |         ^~~~~~~~~~
1 warning generated.
nota.c:6:9: error: unused variable 'neutilizat' [-Werror,-Wunused-variable]
    6 |     int neutilizat;
      |         ^~~~~~~~~~
1 error generated.

Exemplul 3: Un avertisment care previne o greșeală reală

%s așteaptă un șir, dar primește un int. La rulare, programul ar citi memorie de la o adresă greșită. Clang semnalează problema și propune %d.

// varsta.c
#include <stdio.h>
int main(void) {
    int varsta = 20;
    printf("Varsta: %s\n", varsta);
    return 0;
}

// în terminal:
// clang -std=c23 -Wall varsta.c -o varsta

Ieșire în terminal

varsta.c:5:28: warning: format specifies type 'char *' but the argument has type 'int' [-Wformat]
    5 |     printf("Varsta: %s\n", varsta);
      |                     ~~     ^~~~~~
      |                     %d
1 warning generated.

Greșeli frecvente

  • Compilezi fără -Wall -Wextra. Multe greșeli (specificatori printf greșiți, variabile neinițializate, comparații între int și unsigned) apar doar ca avertismente, iar implicit unele sunt ascunse.
  • Uiți -lm pe Linux când folosești sqrt sau pow și primești „undefined reference to `sqrt’”. Pe macOS biblioteca matematică este legată automat. Pune -lm după fișierele sursă.
  • Rulezi programul cu program în loc de ./program. Terminalul caută comenzile în PATH, nu în directorul curent.
  • Compari timpi de rulare compilând cu -O0. Pentru măsurători folosește -O2; -O0 este pentru depanare.

Comenzi înrudite

  • make și Makefile: make citește un Makefile și recompilează doar fișierele modificate, ca să nu scrii de fiecare dată comenzile de compilare.
  • 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.
  • #define: Definește o macro: un nume pe care preprocesorul îl înlocuiește cu textul dat, înainte ca programul să fie compilat.
  • #if, #ifdef, #ifndef: Compilare condiționată: preprocesorul păstrează sau elimină bucăți de cod în funcție de macro definite sau de valoarea lor.
  • Comportament nedefinit (undefined behavior): Situațiile pentru care standardul C nu spune ce se întâmplă: programul poate merge, poate da rezultate greșite sau se poate opri.
  • main: Funcția cu care începe orice program C: sistemul o apelează la pornire, iar valoarea returnată spune dacă programul a reușit.