A C23-ra váltás nem egyszerűen új fordítókapcsoló bekapcsolása. Egyes korábbi fordítóbővítések szabványos nyelvi alakot kaptak, új kulcsszavak jelentek meg, néhány régi konstrukció pedig megváltozott vagy megszűnt. A migráció célja ezért nem az, hogy „leforduljon”, hanem hogy a program helyességi szerződése megmaradjon.
A Szabványok, dialektusok és hordozhatósági szerződés alapján külön kezeljük az ISO C17-et, az ISO C23-at és a fordítóbővítéseket. A változtatásokat a Záróreview: helyesség, diagnosztika és mérés módszerével kell ellenőrizni: rögzített környezetben, pozitív és negatív fordítási próbákkal.
A __STDC_VERSION__ C23-hoz tartozó értéke
202311L. Ez a választott nyelvi verzió jelzése, nem
minden képesség hibátlan megvalósításának bizonyítéka. Egy
régebbi fordító -std=c2x módja pedig nem automatikusan
azonos a végleges C23-támogatással.
Az alábbiakhoz javasolt, itt nem futtatott vizsgálati profil: Linux, x86-64 System V ABI, LP64, GCC 15.1.0 és Clang 20.1.0, glibc 2.41. Ez vizsgálati célkörnyezet, nem teljes támogatási ígéret. A tényleges fordítókiadást, sysrootot és headereket külön rögzíteni kell.
A következő C23-kódrészleteket képességenként külön illeszd ebbe a teljes próbaprogramba:
/* probe.c: a behelyettesített részletek minimuma ISO C23. */
int main(void)
{
/* Egy vizsgált részlet helye. */
return 0;
}Fordítási próbák:
gcc -std=c23 -Wall -Wextra -pedantic-errors -Werror=attributes \
-fsyntax-only probe.c
clang -std=c23 -Wall -Wextra -pedantic-errors -Werror=attributes \
-fsyntax-only probe.cA C17-alternatívákat ugyanezekkel a kapcsolókkal, de
-std=c17 módban vizsgáld. A részletekhez alapvetően nem
kell libc-header; ahol mégis, azt külön jelezzük. A
-fsyntax-only nyelvi és headerpróba, nem linkelési vagy
futási teszt.
ISO C23: a bool, true és
false nyelvi elemek; használatukhoz nem szükséges
<stdbool.h>. A true és
false típusa bool.
/* C23-részlet; két külön képességpróbára is bontandó. */
bool enabled = true;
int *p = nullptr;
(void)enabled;
(void)p;C17-alternatíva: <stdbool.h>
mellett ugyanaz a logikaiérték-szintaxis használható, de makrókon
keresztül: a bool a _Bool típushoz
kapcsolódik, a true és false pedig egész
konstansokra helyettesítődik. Header nélkül
_Bool enabled = 1; írható.
A C23 nullptr előre definiált nullpointer-konstans,
saját típussal. A típus nullptr_t neve a
<stddef.h> headerből érhető el; ehhez külön
headerpróba kell: a header beillesztése után fordítsd a
nullptr_t n = nullptr; deklarációt.
C17-alternatíva: például int *p = 0;.
A nullptr nem jelent foglalt tárolót, és nem teszi
típusbiztonságossá a variadikus hívásokat. Ha egy ilyen interfész
void * argumentumot vár, add át a megfelelő pointertípust,
például (void *)nullptr.
| Pointerhelyzet | Tulajdonos és élettartam | Érvénytelenítés, hibakimenet |
|---|---|---|
p = nullptr |
Nincs hivatkozott objektum vagy birtokolt tároló | Dereferálása nem definiált viselkedés |
| Dinamikus buffer pointere | A felszabadításért felelős programrész a tulajdonos | free és sikeres realloc érvényteleníti a
régi hozzáféréseket |
Migrációs törés lehet egy korábbi int nullptr;
deklaráció is: C17-ben az azonosító megengedett, C23-ban már foglalt
nyelvi elem.
ISO C23: az auto inicializálóból
történő típuslevezetést (type inference) tesz lehetővé. Az
egyszerű hordozható használat egyetlen deklarált objektumot és
inicializálót tartalmaz.
/* C23-részlet. */
auto retries = 3u;
(void)retries;A retries típusa unsigned int; ez
fordításidejű típusválasztás, nem dinamikus típusosság.
C17-alternatíva:
unsigned int retries = 3u;. C17-ben az auto
csak tárolásiosztály-jelölő, például
auto int retries = 3;.
A typeof egy kifejezés vagy típusnév típusát adja meg, a
typeof_unqual pedig annak minősítők nélküli, nem atomikus
változatát:
/* C23-részlet; a két typeof-alak külön is próbálandó. */
const int source = 4;
typeof(source) preserved = 5;
typeof_unqual(source) writable = 6;
writable = 7;
(void)preserved;
(void)writable;A preserved típusa const int, a
writable típusa int.
C17-alternatíva: ezek explicit kiírása; általános,
szabványos typeof operátor nincs.
A GNU-dialektusban korábban elfogadott typeof vagy
__auto_type nem bizonyít ISO C17-támogatást. A fenti
szigorú fordítási próba éppen ezt választja el a szabványos
C23-használattól. A typeof operandusáról változó hosszúságú
tömbtípusok környezetében nem szabad általánosan feltételezni, hogy
biztosan nem értékelődik ki.
constexpr:
C-ben objektum, nem C++-szerű függvényISO C23: a constexpr konstans objektum
definiálására használható; nem vezet be C++-szerű constexpr
függvényeket.
/* C23-részlet. */
constexpr int capacity = 8;
int slots[capacity];
_Static_assert(capacity == 8, "unexpected capacity");
(void)slots;Ebben az egész típusú példában a capacity konstans
kifejezésben használható, ezért a slots nem változó
hosszúságú tömb. A _Static_assert szándékosan a C17-ben is
létező alak: így a próba a constexpr képességet
vizsgálja.
C17-alternatíva: enum { capacity = 8 };
vagy makró. A const int capacity = 8; C17-ben önmagában nem
teszi a nevet egész konstans kifejezésben használhatóvá.
Összehasonlításként ez önálló ISO C++20 program, könyvtári függőség nélkül:
// constexpr_demo.cpp
constexpr int twice(int value)
{
return 2 * value;
}
static_assert(twice(4) == 8);
int main()
{
return 0;
}g++ -std=c++20 -Wall -Wextra -pedantic-errors \
-fsyntax-only constexpr_demo.cpp
clang++ -std=c++20 -Wall -Wextra -pedantic-errors \
-fsyntax-only constexpr_demo.cppA függvényes alak C23-ba másolva nem válik érvényes C-vé. Az azonos kulcsszó eltérő nyelvi képességet jelöl.
ISO C23: a _BitInt(N) bitszélességet
megadó egész típus. Előjeles alakban
,
előjel nélkül
.
Az előjeles alak szélességébe az előjelbit is beleszámít. A támogatott
felső korlátot a <limits.h>
BITINT_MAXWIDTH makrójával, majd konkrét fordítási próbával
kell ellenőrizni.
/* C23-részlet; a típust a literáloktól külön vizsgálja. */
_BitInt(10) signed_value = 300;
unsigned _BitInt(9) unsigned_value = 300;
(void)signed_value;
(void)unsigned_value;Az előjeles, bites tartomány:
Ezért 300 előjeles tárolásához 10 bit kell: , míg . Előjel nélkül 9 bit elegendő.
Bizonyításvázlat: kettes komplemensben az előjelbit súlya , az alsó bitek maximális összege . Ebből adódnak a határok.
C17-alternatíva: megfelelő tartományú hagyományos
egész típus, szükség esetén több szóból felépített reprezentáció. A
uintN_t típusok csak elérhetőségük ellenőrzése után
használhatók, és nem helyettesítenek tetszőleges bitszélességet.
A kapcsolódó C23-újdonságok:
| Képesség és külön fordítandó próba | C17-alternatíva |
|---|---|
int x = 0b1010; – bináris literál |
int x = 0xA; |
int x = 1'024; – számjegyelválasztó |
int x = 1024; |
auto x = 300wb; – előjeles _BitInt
literál |
Megfelelő hagyományos típus és literál |
auto x = 300uwb; – előjel nélküli változat |
Megfelelő előjel nélküli típus |
enum opcode : unsigned char { OP_DATA = 1 }; |
Hagyományos enum, vagy unsigned char typedef és külön
konstansok |
A literálpróbák végére kerüljön (void)x;. A
wb és uwb a szükséges legkisebb megfelelő
bitszélességet választja.
A rögzített mögöttes típusú enum (fixed underlying enum)
értékeinek reprezentálhatónak kell lenniük a megadott típusban. Az
unsigned char azonban nem általánosan „8 bites”: ezt
CHAR_BIT határozza meg. A bitszélességből a
_BitInt tárolómérete, paddingje és ABI-beli átadása sem
következik; ezek külön implementációs vizsgálatok.
static_assertISO C23: szabványos attribútumalak például a
[[maybe_unused]] és a [[nodiscard]].
/* C23-részlet. */
[[maybe_unused]] int reserved = 0;
[[nodiscard]] int check_configuration(void);
static_assert(sizeof(char) == 1);A nodiscard az eredmény figyelmen kívül hagyásának
diagnosztizálását támogatja, nem futásidejű ellenőrzés. A
static_assert kulcsszó, és az üzenet elhagyható.
C17-alternatíva:
_Static_assert(feltetel, "uzenet");; a
static_assert név <assert.h> mellett
makróként használható, két argumentummal. A bemutatott attribútumoknak
nincs általános ISO C17-megfelelőjük: a fordítóspecifikus helyettesítés
külön bővítésszerződés.
Attribútumonként ellenőrizd a __has_c_attribute operátor
elérhetőségét és például a __has_c_attribute(nodiscard)
eredményét. Az elfogadott szintaxis önmagában kevés: az ismeretlen
attribútum figyelmen kívül hagyható. Ezért kell a figyelmeztetést hibává
emelő próba; a static_assert alakjait pedig az
attribútumoktól külön fordítsd.
Függvénydeklarációk. C17-ben az
int ping(); nem közli a paraméterek számát és típusát.
C23-ban nulla paramétert jelent, mint az int ping(void);. A
régi K&R-függvénydefiníciók megszűntek. Mindkét verzióhoz jó
migrációs cél az explicit prototípus.
Negatív próbaként a ping(1) hívásnak C23-ban
diagnosztikát kell kiváltania. C17-beli elfogadása nem bizonyít helyes
hívást. Az implicit int és az előzetes deklaráció nélküli
függvényhívás már C17-ben sem megengedett; ezek nem új
C23-szigorítások.
Kettes komplemens. C23 előírja az előjeles egész típusok kettes komplemens reprezentációját. C17 több reprezentációt megengedett. Ettől az előjeles túlcsordulás továbbra is UB; a migráció nem jogosít fel körbeforduló előjeles aritmetikára. Ezt fordítási siker vagy próbaértékek nem bizonyítják biztonságosnak.
Nulla méretű realloc. A
realloc(p, 0) C17-ben implementáció által meghatározott
kezelésű; C23-ban nem definiált viselkedés. Nem helyes futtatással
„felderíteni”, hogyan működik.
A mindkét verzióban megfelelő javítás: ha a kért méret nulla,
free(p) után állítsd a tulajdonosi pointert nullára.
Egyébként ideiglenes pointerrel hívd a realloc-ot. Nem
nulla méretű sikertelen hívásnál a régi foglalás megmarad; siker esetén
a korábbi kölcsönzött hozzáférések érvényüket vesztik, akkor is, ha a
cím numerikusan változatlan. A pontos minta a realloc és dinamikusan növekvő buffer részben
szerepel.
typeof-ot használó fájl lefordul
-std=gnu17 módban. Mit bizonyít ez, és milyen további
próbák szükségesek?capacity
objektumot és vele rögzített méretű tömböt. Add meg a
C17-alternatívát.int flush();
deklarációt, K&R-definíciót és
realloc(buffer, requested) hívást használ. A
requested lehet nulla. Milyen javítások és ellenőrzések
kellenek?Csak az adott GNU-dialektus támogatása bizonyított. Külön próbáld
-std=c17 -pedantic-errors és
-std=c23 -pedantic-errors mellett, mindkét rögzített
fordítóval. Az ellenőrzési feltétel a dialektusfüggő és a szabványos
elfogadás elkülönítése.
C23-ban
constexpr int capacity = 16; int data[capacity];. C17-ben
enum { capacity = 16 }; int data[capacity];. Adj hozzá
_Static_assert(capacity == 16, "capacity"); ellenőrzést.
Mindkét változatnak a saját nyelvi módjában kell fordulnia; C17-ben egy
puszta const int nem egyenértékű helyettesítés.
Írj int flush(void); deklarációt, és alakítsd a
K&R-definíciót típusos paraméterlistájúvá. A nulla méretet külön
felszabadítási ág kezelje. Ellenőrizd a nulla, a nem nulla sikeres és az
injektált allokációs hibás esetet; továbbá azt, hogy az érvénytelenített
kölcsönzött pointereket senki sem használja. A hibás nulla méretű hívást
ne futtasd C23-as tesztként.
A C23-váltás képességenkénti döntés. Minden új konstrukcióhoz
tartozzon C17-alternatíva, explicit nyelvi mód és elkülönített
támogatáspróba. Különösen vizsgáld az új kulcsszavakkal ütköző neveket,
a prototípus nélküli deklarációkat és a nulla méretű
realloc-ot. A modernebb szintaxis nem helyettesíti az
ownership-, élettartam- és túlcsordulási szerződést.