A C23 könyvtári újdonságai több, rendszerprogramozásban visszatérő művelethez adnak szabványos eszközt: ellenőrzött egész aritmetikához, bitvizsgálathoz és érzékeny adatok törléséhez. Használatuk azonban nem következik pusztán a C23 nyelvi mód bekapcsolásából. A preprocessort, a fordítót, a headereket és a linkeléskor, illetve futáskor használt könyvtárat külön kell vizsgálni.
A C23 nyelvi újdonságok és inkompatibilis változások rész verziózási alapjaira építünk; a ritkább numerikus szolgáltatások előzménye a Numerikus és ritkább C könyvtári eszközök térképe. Itt a döntési szabály a fontos: képességet ellenőrizzünk, ne támogatást feltételezzünk egy verziószámból.
Az ISO C23 nyelvi verziót a
__STDC_VERSION__ makró 202311L értéke jelzi.
Ez nem szolgáltatásonkénti megfelelőségi teszt. Korábbi fordítókiadások
részleges C23-támogatása más verzióértékkel vagy -std=c2x
kapcsolóval is megjelenhet; ezeket külön profilként kell kezelni.
A képességvizsgálat (feature detection) három eltérő kérdést választ szét:
| Vizsgálat | Mit igazol? | Mit nem igazol? |
|---|---|---|
| Nyelvi fordítási próba | A fordító elfogadja a használt konstrukciót | Egy könyvtári függvény elérhetőségét |
| Header és konkrét API fordítása | A deklaráció vagy makró használható a megadott típusokkal | Külső függvényszimbólum linkelhetőségét |
| Linkelési, szükség esetén célgépi próba | A kiválasztott könyvtári környezetben használható a szolgáltatás | Más telepítési környezet kompatibilitását |
A headert akár a fordítócsomag is szállíthatja, nem feltétlenül a libc. Ez különösen fontos a fordítói beépített műveletekre épülő headereknél.
A példák választott laborprofilja Linux, x86-64
System V ABI, LP64, CHAR_BIT == 8, GCC 15.1.0 vagy Clang
20.1.0, glibc 2.41. Ez vizsgálati célprofil, nem közölt fordítási
eredmény. A parancsokban szereplő gcc a rögzített kiadást
jelentse; Clanggal ugyanazokat a próbákat külön kell elvégezni.
Az ISO C23 <stdckdint.h>
headerének ckd_add, ckd_sub és
ckd_mul makrói ellenőrzött egész aritmetikát végeznek. A
matematikai eredmény ábrázolhatóságát a célobjektum típusában
vizsgálják; igaz visszatérési érték jelzi az ábrázolhatatlanságot. Ez
nem egyszerűen az operandusok szokásos közös típusában végzett művelet
utólagos ellenőrzése.
Az alábbi példa minden operandushoz és célobjektumhoz
size_t típust használ. Hiba esetén a belső eredményt nem
teszi közzé.
Az ISO C23 <stdbit.h> unsigned
egész értékek bitmintázatának vizsgálatát támogatja. Például:
stdc_count_ones_ui(x): az egyes bitek száma egy
unsigned int értékben;stdc_bit_width_ui(x): a reprezentáláshoz szükséges
értékbitek száma; nullára nulla;stdc_has_single_bit_ui(x): pontosan egy bit van-e
beállítva, vagyis az érték pozitív kettőhatvány-e.Ezek értékműveletek, nem memóriába írt bájtsorrendek vizsgálatai. Nem helyettesítik a hálózati szerializálást.
Egy keret tárolóigénye legyen
ahol
az elemszám,
az elemméret C bájtban,
pedig a fejléc mérete. Mindhárom nemnegatív, size_t
érték.
A következő teljes program alapértelmezésben ISO C17; a két konfigurációs kapcsoló külön engedélyezi a C23-szolgáltatásokat.
/* capacity.c */
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#ifndef HAVE_C23_CHECKED
#define HAVE_C23_CHECKED 0
#endif
#ifndef HAVE_C23_BITS
#define HAVE_C23_BITS 0
#endif
#if HAVE_C23_CHECKED
#include <stdckdint.h>
#endif
#if HAVE_C23_BITS
#include <stdbit.h>
#endif
static bool frame_size(size_t n, size_t w, size_t h, size_t *out)
{
size_t product;
size_t total;
#if HAVE_C23_CHECKED
if (ckd_mul(&product, n, w) ||
ckd_add(&total, product, h)) {
return false;
}
#else
if (w != 0 && n > SIZE_MAX / w) {
return false;
}
product = n * w;
if (h > SIZE_MAX - product) {
return false;
}
total = product + h;
#endif
*out = total;
return true;
}
static unsigned int ones(unsigned int value)
{
#if HAVE_C23_BITS
return stdc_count_ones_ui(value);
#else
unsigned int count = 0;
while (value != 0u) {
value &= value - 1u;
++count;
}
return count;
#endif
}
int main(void)
{
size_t bytes = 0;
if (!frame_size(4, 24, 16, &bytes) || bytes != 112)
return EXIT_FAILURE;
if (frame_size(SIZE_MAX, 2, 0, &bytes) || bytes != 112)
return EXIT_FAILURE;
if (ones(0xA4u) != 3u)
return EXIT_FAILURE;
return EXIT_SUCCESS;
}Az out előfeltétele: nem null pointer, és élő, írható
size_t objektumra mutat; az objektumhoz nincs
szinkronizálatlan konkurens hozzáférés.
| Hozzáférés | Tulajdonos és élettartam | Érvénytelenítés, hibakimenet |
|---|---|---|
out |
A hívó birtokolja; a függvény csak a hívás alatt kölcsönzi | Hiba esetén változatlan; a pointert a függvény nem őrzi meg |
&product, &total |
A frame_size lokális objektumai |
Élettartamuk a függvényből kilépéskor véget ér |
Bizonyításvázlat. Legyen . Ha , akkor
Ezért a szorzás előtt osztással vizsgálható az ábrázolhatóság. Ha , a szorzat nulla. A már ellenőrzött után
A kivonás biztonságos, mert . A fallback tehát nem előbb számol túlcsorduló eredményt, hanem a művelet előtt ellenőriz.
A bitfallback minden iterációban törli a legalsó beállított bitet: a
value - 1u az addigi legalsó egyest nullává, az alatta levő
nullákat egyessé változtatja; az ÉS-művelet csak azt az egyest
törli.
Fordítás és a két képesség külön próbája:
gcc -std=c17 -Wall -Wextra -Wpedantic capacity.c -o capacity-c17
gcc -std=c23 -Wall -Wextra -Wpedantic \
-DHAVE_C23_CHECKED=1 capacity.c -o capacity-checked
gcc -std=c23 -Wall -Wextra -Wpedantic \
-DHAVE_C23_BITS=1 capacity.c -o capacity-bitsA várt sikeres futási eredmény nulla kilépési státusz; ez nem mért eredmény. A konfiguráció csak sikeres próbák után állítsa egyre a megfelelő kapcsolókat. A két képességet ne egyetlen „van C23” kapcsoló helyettesítse.
Az ISO C23 <string.h> headerében
megjelenő memset_explicit rendeltetése, hogy a törlő írás
ne maradjon el pusztán azért, mert az objektumot később már nem
olvassák. A közönséges memset ilyen körülmények között
eltávolítható optimalizáláskor.
A szerződés továbbra is érvényes, írható, megfelelő méretű tárolót követel. A titok tulajdonosa töröljön felszabadítás előtt, miután megszűnt minden párhuzamos hozzáférés. A művelet nem törli automatikusan a korábbi másolatokat, regiszterben maradt értékeket, core dumpot vagy swapot.
C17 fallback: nincs azonos garanciájú, általánosan
rendelkezésre álló ISO C17-függvény. Használható dokumentált
platformprimitív, külön képességpróbával. Ha az alkalmazás biztonsági
szerződése megköveteli a törlést, megfelelő primitív hiányában a
konfiguráció hibázzon. A sima memset és egy ellenőrizetlen
„volatile trükk” nem tekinthető automatikusan egyenértékű
helyettesítésnek.
A strdup és strndup szintén ISO
C23 könyvtári függvény; korábban POSIX
környezetben voltak szabványosak, ISO C17-ben nem.
A strdup nullterminált bemenetet vár. A
strndup(s, n) legfeljebb az első
karaktert másolja, korábbi nullkarakternél megáll, és siker esetén
terminátort ad az eredményhez. A bemenetnek a vizsgált tartományban
olvashatónak kell lennie; a korlát nem tesz érvényessé egy dangling
pointert.
Mindkettő új allokációt ad vissza: siker esetén a hívó tulajdona,
free érvényteleníti; allokációs hibánál null pointert
kapunk. C17-ben saját nevű wrapper készíthető: korlátozott hosszkeresés,
az len + 1 méret ellenőrzése, malloc, másolás,
terminálás. Ne deklaráljuk kézzel a hiányzó szabványos nevet.
Az ISO C23 __has_include azt vizsgálja,
hogy egy header az adott keresési környezetben bevonható-e. Nem igazolja
sem az API teljességét, sem a külső függvények linkelhetőségét. Régebbi
preprocesszorokhoz először magának az operátornak az elérhetőségét is
vizsgálni kell; a konfigurált fordítási próba gyakran egyszerűbb.
További C23-eszközök:
#embed fordításkor külső erőforrásból állít elő
inicializálásban használható adatot. A build függőségei közé az
erőforrásfájlt is fel kell venni; ez nem futásidejű fájlolvasás. A
limit(...) korlátozhatja a bevont adatmennyiséget, a
__has_embed előzetes vizsgálatot támogat.#elifdef és #elifndef a megfelelő
#elif defined(...), illetve negált alak rövidítése.#warning szabványosított diagnosztikai direktíva; a
build warning-policyja határozza meg, hogy megállítja-e a
fordítást.__VA_OPT__ variadikus makróban csak akkor illeszti be
argumentumát, ha a variadikus rész makróhelyettesítés után nem
üres.C23-as, önállóan nem teljes programrészlet:
#define VALUES(...) { 0 __VA_OPT__(,) __VA_ARGS__ }
static const int first[] = VALUES();
static const int second[] = VALUES(4, 9);A két inicializáló rendre { 0 } és
{ 0, 4, 9 }. C17 fallback lehet két külön makrófelület. A
#embed helyett előállított C-inicializáló használható,
reprodukálható generálási szabállyal. A C17-es ágból a C23-direktívákat
célszerű külön fájlba választani; a preprocesszor új képességeinek
felismerését ne ugyanarra a képességre építsük.
A C23 könyvtári térképen külön vizsgálandók a következők:
| Terület | Példa | Ellenőrzési szempont |
|---|---|---|
| Numerika | roundeven, további matematikai műveletek |
Deklaráció, linkelés, szükség esetén libm |
| Feltételes lebegőpontos képességek | Decimális lebegőpontos típusok és kapcsolódó támogatás | Fordítói és könyvtári képesség külön |
| Karakterkonverzió | <uchar.h>, mbrtoc8,
c8rtomb |
Locale- és konverziósállapot-szerződés |
| Méretezett felszabadítás | free_sized, free_aligned_sized |
A méret és igazítás pontos előfeltétele |
A „külön vizsgálandó” nem jelenti, hogy minden felsorolt szolgáltatás szabvány szerint opcionális. A feltételes szabványos képességet és a hiányos implementációt meg kell különböztetni.
Gyakori hiba a header jelenlétét teljes támogatásnak tekinteni, a túlcsordulást a művelet után ellenőrizni, vagy biztonsági garanciát gyengébb fallbackkal észrevétlenül lecserélni. Ezeket külön konfigurációs kapcsoló, előzetes aritmetikai ellenőrzés és dokumentált hibapolitika javítja.
-std=c23 és a
<string.h> sikeres bevonása a
memset_explicit használatához?frame_size(SIZE_MAX, 0, 7, ...) és a
frame_size(1, 1, SIZE_MAX, ...) esetekkel.memset_explicit helyére
memset kerül, a strndup támogatását pedig csak
headerellenőrzéssel döntik el. Diagnosztizáld és javítsd a
konfigurációt.A nyelvi mód nem biztosít libc-szimbólumot, a
<string.h> pedig C17-ben is létezik. A konkrét
függvény deklarációját fordítással, szimbólumát linkeléssel kell
ellenőrizni; dinamikus linkelésnél a célkörnyezet is számít. Ellenőrzési
feltétel: a próba valóban hivatkozzon a függvényre, és ezt az
optimalizáló ne tüntesse el.
A következő C17-kompatibilis részlet a korábbi main
függvénybe illeszthető, ugyanazokkal a buildparancsokkal:
if (!frame_size(SIZE_MAX, 0, 7, &bytes) || bytes != 7)
return EXIT_FAILURE;
if (frame_size(1, 1, SIZE_MAX, &bytes) || bytes != 7)
return EXIT_FAILURE;Az első esetben a szorzat nulla, tehát az eredmény hét. A másodikban nem ábrázolható. Az ellenőrzés a hibajelzés mellett a változatlan kimeneti objektumot is vizsgálja.
A memset nem őrzi meg az explicit törlés garanciáját.
Dokumentált platformfallback vagy konfigurációs hiba szükséges. A
strndup számára konkrét fordítási és linkelési próba kell;
C17/POSIX profilban az előírt feature-test makrókat a headerek előtt
kell beállítani. Ellenőrzési feltétel: minden engedélyezett és fallback
ág külön buildben, azonos alkalmazásszintű szerződéssel működjön.
A C23-eszközök választását műveleti szerződés vezesse: ellenőrzött
aritmetikához stdckdint.h, bitvizsgálathoz
stdbit.h, érzékeny törléshez megfelelő garanciájú primitív
kell. A compiler, a headerek és a libc képessége külön bizonyítandó. A
jó C17 fallback a viselkedést őrzi meg; ahol ez nem biztosítható, a
hiányt látható hibává teszi.