Un feature mai obscur din C

Recent am dat peste ceva ce m-a surprins în C, dacă zic:

void f(int arr[static 100]);

Înseamnă că arr are minim 100 de elemente. 6.7.6.3/7:

A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to
type’’, where the type qualifiers (if any) are those specified within the [ and ] of the
array type derivation. If the keyword static also appears within the [ and ] of the
array type derivation, then for each call to the function, the value of the corresponding
actual argument shall provide access to the first element of an array with at least as many elements as specified by the size expression.

C++ nu are asta.

1 Like

Din păcate nu rezolva multe.

Daca ii dai un null, crapă.

Daca ii dai un array mai mic de cate elemente ai specificat cu static e undefined behaviour.

E genul de chestie care ajuta compilatorul sa optimizeze codul. Adică nu as expune o funcție așa într-un API public, dar pentru funcții cu uz intern unde ai controlul și garanția că sunt apelate cum vrei, e ok.

Sunt mai multe chestii care merg in C și nu merg in C++, ce e drept minore. De exemplu în C trebuie sa faci cast după malloc, in C nu mai e nevoie (nici nu știu dacă a fost vreodată nevoie).

Daca folosești un compilator de C++ sunt șanse mari să îți compileze C, dar nu obligatoriu.

1 Like

Evident, nu trebuie abuzat de chestia asta. Doar că m-a dat puțin pe spate când am văzut-o, nu mă așteptam să poți zice static între paranteze drepte…

Exemplul cu cast la malloc e clasic, dar mai sunt diferențe (părerea mea nu chiar minore):

  • Header-ele de genul <stdio.h> vs. <cstdio>. Cele din urmă au diferența că funcțiile nu pot fi implementate ca macro, de exemplu în C putc poate să fie implementat ca un macro, pe când std::putc obligatoriu nu este implementat ca macro.
  • malloc e mai dificil de folosit corect în C++ decât în C. În C++ ceva de genul:
    int* value = reinterpret_cast<int*>(malloc(sizeof(int)));
    *value;
    
    Teoretic e undefined-behavior. Practic pentru cazul ăsta cam orice compilator o să facă ce trebuie, dar problemele mai mari apar când există și constructori. Parcă în C++20 s-a mai schimbat ceva dar tot n-aș asuma nimic. Modul safe de a utiliza malloc în C++ e cu placement-new după:
    void* mem = malloc(sizeof(int));
    int* value = new(mem) int{};
    
    Evident, există new, dar dacă este nevoie de memorie alocată dar fără să fie nevoie de constructori apelați cam asta e singura soluție, și nu e chiar obvious modul corect de utilizare (IMO).
  • În C++ e valid să ai return cu o expresie care întoarce void:
    void f();
    
    void g() { return f(); }
    
    Pe când C nu acceptă asta.

Mai sunt și alte diferențe, dar acestea sunt câteva care mi s-au părut mai non-obvious.

1 Like