Ce face #if, #ifdef, #ifndef
Directivele #if, #ifdef și #ifndef aleg ce cod ajunge la compilator. Codul dintr-o ramură falsă este eliminat înainte de compilare, ca și cum nu ar fi fost scris. Așa se fac versiunile de depanare (#ifdef DEBUG), codul diferit pentru Windows și Linux și gărzile de includere.
#if evaluează o expresie cu numere întregi și macro. defined(NUME) este 1 dacă macro-ul există. #ifdef NUME este prescurtarea pentru #if defined(NUME), iar #ifndef pentru opusul ei. C23 adaugă #elifdef și #elifndef, plus __has_include(<antet>), care verifică dacă un antet există.
#error "mesaj" oprește compilarea cu mesajul dat, util când lipsește o configurare obligatorie. #warning afișează doar un avertisment; era o extensie a compilatoarelor și a devenit standard în C23.
Parametri
| Parametru | Descriere |
|---|---|
defined(NUME) |
1 dacă macro-ul NUME este definit, 0 altfel. Se poate combina: #if defined(A) && !defined(B). |
__has_include(...) |
C23: 1 dacă antetul poate fi inclus. Exemplu: #if __has_include(<stdckdint.h>). |
Exemple
Exemplul 1: Mesaje de depanare doar când este definit DEBUG
Aici DEBUG este definit în cod. În practică se lasă afară și se pornește din linia de comandă cu clang -DDEBUG program.c. Fără el, linia cu „[debug]” nici nu ajunge în program.
#include <stdio.h>
#define DEBUG
#define NIVEL 2
int main(void) {
int note[] = {9, 7, 10};
int suma = 0;
for (int i = 0; i < 3; i++) {
suma += note[i];
#ifdef DEBUG
printf("[debug] după nota %d, suma = %d\n", i + 1, suma);
#endif
}
#if NIVEL >= 2
printf("Nivel detaliat: %d note\n", 3);
#endif
printf("Media: %.2f\n", suma / 3.0);
return 0;
}
Rezultat
[debug] după nota 1, suma = 9
[debug] după nota 2, suma = 16
[debug] după nota 3, suma = 26
Nivel detaliat: 3 note
Media: 8.67
Exemplul 2: Cod diferit pentru fiecare sistem de operare
Compilatorul definește singur macro precum _WIN32, __linux__ sau __APPLE__. Exemplul a fost compilat pe macOS, deci a rămas ramura #elifdef __APPLE__ (C23). __has_include verifică dacă antetul C23 <stdckdint.h> există.
#include <stdio.h>
#ifdef _WIN32
#define SISTEM "Windows"
#elifdef __linux__
#define SISTEM "Linux"
#elifdef __APPLE__
#define SISTEM "macOS"
#else
#define SISTEM "alt sistem"
#endif
#if __has_include(<stdckdint.h>)
#define ARE_CKDINT 1
#else
#define ARE_CKDINT 0
#endif
int main(void) {
printf("Compilat pentru: %s\n", SISTEM);
printf("Are <stdckdint.h>: %s\n", ARE_CKDINT ? "da" : "nu");
return 0;
}
Rezultat
Compilat pentru: macOS
Are <stdckdint.h>: da
Exemplul 3: Oprirea compilării cu #error
Dacă versiunea configurată este prea mică, #error oprește compilarea cu mesajul tău. Programul nu se construiește deloc.
#define VERSIUNE_CONFIG 2
#if VERSIUNE_CONFIG < 3
#error "Ai nevoie de versiunea 3 a configurației sau mai nouă"
#endif
int main(void) {
return 0;
}
Mesajul compilatorului (programul nu se compilează)
exemplu.c:4:2: error: "Ai nevoie de versiunea 3 a configurației sau mai nouă"
4 | #error "Ai nevoie de versiunea 3 a configurației sau mai nouă"
| ^
1 error generated.
Greșeli frecvente
- Uiți
#endif. Compilatorul raportează „unterminated conditional directive” la linia cu#if, nu acolo unde lipsește#endif. - Scrii
#if DEBUGcândDEBUGa fost definit fără valoare (#define DEBUG). Expresia devine goală și compilarea dă eroare. Pentru verificarea existenței folosește#ifdef DEBUGsau#if defined(DEBUG). - Folosești
#ifcu variabile din program (#if nota > 5). Preprocesorul rulează înainte de compilare și nu știe nimic despre variabile; un nume necunoscut valorează 0.
Comenzi înrudite
#define: Definește o macro: un nume pe care preprocesorul îl înlocuiește cu textul dat, înainte ca programul să fie compilat.#include: Copiază conținutul unui fișier antet (.h) în locul liniei, ca funcțiile și tipurile declarate acolo să poată fi folosite.Macro cu parametri: Macro care primesc argumente, ca o funcție: textul lor este copiat în locul apelului, cu operatorii # și ## și argumente variabile.static_assert: Verifică o condiție la compilare: dacă este falsă, programul nu se compilează și apare mesajul tău.Compilarea cu gcc și clang: Cum transformi codul C într-un program executabil cu gcc sau clang și ce fac opțiunile folosite cel mai des.