constexpr în C

Declară o constantă cu valoare calculată la compilare, cu tip, care poate fi folosită ca dimensiune de tablou sau în static_assert.

Sintaxă
constexpr int MAX_ELEVI = 30;
constexpr double TVA = 0.21;

Ce face constexpr

În C, o variabilă const int nu este o „constantă de compilare”: nu poți declara cu ea un tablou global și nici nu o poți folosi în case. De aceea, pentru astfel de valori se foloseau #define sau enum. C23 adaugă constexpr: valoarea este cunoscută la compilare, are un tip verificat și se poate folosi oriunde este nevoie de o constantă.

Inițializarea unui constexpr trebuie să fie o expresie constantă (literali, alte constexpr, sizeof, operații între ele). Dacă folosești o variabilă obișnuită, compilatorul refuză. Valoarea trebuie să încapă exact în tip: constexpr unsigned char x = 300; este eroare, nu trunchiere.

Spre deosebire de C++, în C23 constexpr se aplică doar obiectelor (variabilelor), nu și funcțiilor. Pentru o funcție mică, folosește static inline.

Exemple

Exemplul 1: Dimensiune de tablou și etichete case

NR_ELEVI fixează dimensiunea tabloului global, iar NOTA_MAXIMA apare într-un case. Cu un simplu const int, ambele ar fi dat eroare în C.

#include <stdio.h>

constexpr int NR_ELEVI = 4;
constexpr int NOTA_MAXIMA = 10;

int note[NR_ELEVI] = {10, 6, 9, 10};

int main(void) {
    for (int i = 0; i < NR_ELEVI; i++) {
        switch (note[i]) {
        case NOTA_MAXIMA:
            printf("Elevul %d: nota maximă!\n", i + 1);
            break;
        default:
            printf("Elevul %d: %d\n", i + 1, note[i]);
        }
    }
    return 0;
}

Rezultat

Elevul 1: nota maximă!
Elevul 2: 6
Elevul 3: 9
Elevul 4: nota maximă!

Exemplul 2: Constante calculate din alte constante

PRET_CU_TVA este calculat de compilator din alte două constexpr. static_assert verifică la compilare că garanția are cel puțin un an. Atenție: static_assert acceptă doar expresii cu numere întregi, deci nu poate compara direct cele două prețuri double.

#include <stdio.h>

constexpr double PRET_BAZA = 250.0;
constexpr double TVA = 0.21;
constexpr double PRET_CU_TVA = PRET_BAZA * (1 + TVA);
constexpr int ZILE_GARANTIE = 2 * 365;

static_assert(ZILE_GARANTIE >= 365, "garanția minimă este de un an");

int main(void) {
    printf("Preț cu TVA: %.2f lei\n", PRET_CU_TVA);
    printf("Garanție: %d zile\n", ZILE_GARANTIE);
    return 0;
}

Rezultat

Preț cu TVA: 302.50 lei
Garanție: 730 zile

Exemplul 3: Valoarea trebuie să fie cunoscută la compilare

n este o variabilă obișnuită; valoarea ei se știe abia la rulare, deci nu poate inițializa un constexpr.

#include <stdio.h>

int main(void) {
    int n = 3;
    constexpr int maxim = n * 10;
    printf("%d\n", maxim);
    return 0;
}

Mesajul compilatorului (programul nu se compilează)

exemplu.c:5:27: error: constexpr variable 'maxim' must be initialized by a constant expression
    5 |     constexpr int maxim = n * 10;
      |                           ^~~~~~
1 error generated.

Greșeli frecvente

  • Scrii constexpr int dublu(int x) { ... } ca în C++. În C23, constexpr nu se aplică funcțiilor.
  • Crezi că const și constexpr sunt același lucru. const înseamnă doar „nu modific valoarea”, care poate veni și de la rulare; constexpr cere o valoare cunoscută la compilare.
  • Inițializezi un constexpr cu o valoare care nu încape în tip (constexpr unsigned char c = 300;). În loc de trunchiere tăcută, primești eroare de compilare.

Comenzi înrudite

  • const: Marchează o variabilă ca doar pentru citire: compilatorul oprește orice încercare de a o modifica.
  • #define: Definește o macro: un nume pe care preprocesorul îl înlocuiește cu textul dat, înainte ca programul să fie compilat.
  • static_assert: Verifică o condiție la compilare: dacă este falsă, programul nu se compilează și apare mesajul tău.
  • enum: Definește un set de constante întregi cu nume, de exemplu zilele săptămânii sau stările unei comenzi.
  • switch: Alege una din mai multe ramuri după valoarea unui întreg, cu etichete case; fără break, execuția continuă în ramura următoare.
  • Tablouri (array): Un șir de elemente de același tip, așezate unul după altul în memorie și accesate prin index, de la 0.