static_assert în C

Verifică o condiție la compilare: dacă este falsă, programul nu se compilează și apare mesajul tău.

Sintaxă
static_assert(expresie_constanta);              // C23
static_assert(expresie_constanta, "mesaj");
_Static_assert(expresie_constanta, "mesaj");   // C11

Ce face static_assert

assert verifică o condiție la rulare. static_assert o verifică la compilare: dacă expresia este 0, compilarea se oprește cu eroare. Nu costă nimic la rulare, pentru că după compilare nu mai rămâne nimic din ea.

Expresia trebuie să fie constantă: sizeof, constante constexpr, enum, macro. Se folosește ca să verifici presupuneri despre platformă (sizeof(int) == 4), dimensiunea unei structuri care se scrie pe disc sau se trimite în rețea, ori că un tablou are tot atâtea elemente câte valori are un enum.

În C11 se scria _Static_assert(expresie, "mesaj"), iar static_assert era o macro din <assert.h>. În C23, static_assert este cuvânt cheie și mesajul este opțional.

Exemple

Exemplul 1: Presupuneri despre platformă

Toate verificările sunt adevărate pe calculatorul de build, așa că programul se compilează și rulează. Pe o platformă unde int ar avea alt număr de octeți, compilarea s-ar opri.

#include <stdint.h>
#include <stdio.h>

static_assert(sizeof(int) == 4, "codul presupune int pe 32 de biți");
static_assert(sizeof(int64_t) == 8);
static_assert(-1 >> 1 == -1, "deplasarea la dreapta trebuie să fie aritmetică");

int main(void) {
    printf("Toate verificările au trecut la compilare\n");
    return 0;
}

Rezultat

Toate verificările au trecut la compilare

Exemplul 2: Tabloul trebuie să aibă un text pentru fiecare valoare din enum

Dacă cineva adaugă o zi în enum și uită textul ei, static_assert oprește compilarea în loc să lase un element lipsă în tablou.

#include <stdio.h>

enum zi { LUNI, MARTI, MIERCURI, JOI, VINERI, NR_ZILE };

static const char *nume_zi[] = {"luni", "marți", "miercuri", "joi", "vineri"};

static_assert(sizeof nume_zi / sizeof nume_zi[0] == NR_ZILE,
              "fiecare zi din enum are nevoie de un nume");

int main(void) {
    for (int z = LUNI; z < NR_ZILE; z++) {
        printf("%d: %s\n", z, nume_zi[z]);
    }
    return 0;
}

Rezultat

0: luni
1: marți
2: miercuri
3: joi
4: vineri

Exemplul 3: O verificare care nu trece

Pe calculatoarele obișnuite, int are 4 octeți, deci condiția este falsă. Clang arată mesajul și chiar valoarea calculată ('4 == 8').

static_assert(sizeof(int) == 8, "int trebuie să aibă 8 octeți");

int main(void) {
    return 0;
}

Mesajul compilatorului (programul nu se compilează)

exemplu.c:1:15: error: static assertion failed due to requirement 'sizeof(int) == 8': int trebuie să aibă 8 octeți
    1 | static_assert(sizeof(int) == 8, "int trebuie să aibă 8 octeți");
      |               ^~~~~~~~~~~~~~~~
exemplu.c:1:27: note: expression evaluates to '4 == 8'
    1 | static_assert(sizeof(int) == 8, "int trebuie să aibă 8 octeți");
      |               ~~~~~~~~~~~~^~~~
1 error generated.

Greșeli frecvente

  • Pui în static_assert o variabilă obișnuită (static_assert(n > 0)). Valoarea ei nu se știe la compilare, deci primești eroare. Pentru verificări la rulare folosește assert.
  • Confunzi static_assert cu assert: -DNDEBUG dezactivează assert, dar nu are niciun efect asupra lui static_assert.

Comenzi înrudite

  • assert: Verifică în timpul rulării o condiție care trebuie să fie mereu adevărată și oprește programul cu un mesaj dacă nu este.
  • constexpr: Declară o constantă cu valoare calculată la compilare, cu tip, care poate fi folosită ca dimensiune de tablou sau în static_assert.
  • sizeof: Operatorul care spune câți octeți ocupă un tip sau o variabilă; calculat de compilator, rezultatul are tipul size_t.
  • enum: Definește un set de constante întregi cu nume, de exemplu zilele săptămânii sau stările unei comenzi.
  • #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.