A „C-ben írt program” önmagában még nem pontos technikai meghatározás. Tudnunk kell, melyik nyelvváltozat szabályait követi, milyen könyvtárakra támaszkodik, és milyen platformfeltételeket vár el. Ugyanaz a forrás lefordulhat két gépen úgy, hogy eltérő szolgáltatások állnak rendelkezésére, vagy egyes típusok más reprezentációt kapnak.
Az Absztrakt gép, forrásprogram és natív folyamat különválasztotta a nyelvi jelentést és a tényleges végrehajtást. Most ezt hordozhatósági szerződéssé alakítjuk: rögzítjük, mit követelhet a program a nyelvtől, a könyvtártól és a környezettől.
A C példák alapja ISO C17, a C++ példáké ISO C++20. A C23 és C++23 képességeket külön jelöljük. C és C++ két külön nyelv: a C++ nem egyszerűen egy másik kapcsolóval fordított C.
GCC/Clang eszközprofilban a nyelvi módot mindig explicit választjuk:
gcc -std=c17 -Wall -Wextra -pedantic-errors profile.c -o profile-gcc
clang -std=c17 -Wall -Wextra -pedantic-errors profile.c -o profile-clang
g++ -std=c++20 -Wall -Wextra -pedantic-errors capability.cpp -o capability-gcc
clang++ -std=c++20 -Wall -Wextra -pedantic-errors capability.cpp -o capability-clangEzek fordítási parancsok a későbbi teljes példákhoz. A
figyelmeztetések hasznos diagnosztikát adnak; a
-pedantic-errors a választott szabványhoz kapcsolódó
kötelező diagnosztikák szigorítását szolgálja. A sikeres
fordítás egyik kapcsolókészlettel sem bizonyítja a program helyességét
vagy teljes hordozhatóságát.
A gnu17 és gnu++20 GNU dialektusok:
szabványos alaphoz fordítóbővítéseket adnak. Használatuk lehet indokolt,
de külön szerződésként kell megjelenniük. Az ISO mód sem jelenti
automatikusan minden bővítés elutasítását.
A könyv profiljait névvel azonosítjuk, és egyetlen helyen, a központi profiltáblaban rögzítjük: a
gcc/g++ parancsok a lab-gcc, a
clang/clang++ parancsok a
lab-clang profilhoz tartoznak; a C23, C++23 és C++26
kitekintés a lab-next profilt használja. A Clang által
kiválasztott C++ könyvtárat a buildnaplóban külön rögzíteni kell.
Buildjegyzőkönyv: a lenti profile.c és
capability.cpp a fenti GCC-parancsokkal a
lab-gcc profilban hiba és figyelmeztetés nélkül fordult; a
profile-gcc kimenete __STDC_VERSION__=201710,
a capability-gcc kimenet nélkül, 0 kóddal
futott. A Clang-sorokat nem ellenőriztük.
A szabványév nyelvi és könyvtári követelményrendszert jelöl, nem egy fordító kiadási évét. Az alábbi térkép tájékozódási segítség, nem teljes változáslista.
| Nyelvváltozat | Jellegzetes változás | Szabványos verziójelző |
|---|---|---|
| C89 / ISO C90 | Az ANSI C, majd ISO C alapjainak rögzítése | __STDC_VERSION__ még nincs |
| C99 | Például //, long long,
<stdint.h> |
199901L |
| C11 | Atomikus műveletek, szálkezelési könyvtár,
_Generic |
201112L |
| C17 | Elsősorban hibajavítások és pontosítások | 201710L |
| C23 | Például nullptr, typeof, új könyvtári
elemek |
202311L |
| C++11 | Mozgatás, lambdák, konkurenciamodell | 201103L |
| C++14 | Többek között generikus lambdák, bővebb constexpr |
201402L |
| C++17 | Például strukturált kötés, if constexpr,
filesystem |
201703L |
| C++20 | Például concepts, ranges, coroutine-ok | 202002L |
| C++23 | Például std::expected, std::print |
202302L |
A C sorainál a jelző __STDC_VERSION__, a C++ sorainál
__cplusplus. A C verziómakrója a C95 módosítással jelent
meg, 199409L értékkel.
A végleges szabványhoz rendelt érték nem jelenti azt, hogy minden részleges implementáció ezt jelenti: fejlesztés alatt álló módok korábbi, átmeneti értéket is használhatnak. C11-ben egyes új szolgáltatások támogatása opcionális volt; a szabványév ezért önmagában nem megfelelő képességvizsgálat.
Egy állítás mellé oda kell tudnunk írni, melyik szerződésből következik.
| Réteg | Mit határoz meg? | Példa |
|---|---|---|
| ISO nyelv | Típusok, kifejezések, objektumok, élettartam szabályai | Előjeles egész túlcsordulásának megítélése |
| ISO standard könyvtár | A nyelvi szabvány könyvtári felülete | C-ben printf, C++-ban std::vector |
| POSIX | Operációsrendszer-szolgáltatások hordozható felülete | fork, read,
pthread_create |
| Linux | Linuxhoz kötődő felületek és viselkedés | epoll |
| ABI | Bináris illesztési szerződés | Paraméterátadás, adattípus-reprezentáció, objektumformátumhoz kapcsolódó szabályok |
| Fordítóbővítés | Az adott implementáció szabványon túli képessége | GNU statement expression |
Az ISO standard könyvtár is a nyelvi szabvány része; azért választjuk külön, mert implementációja és kiadási ciklusa eltérhet a fordító nyelvi elemzőjétől.
Az ABI, vagyis Application Binary Interface nem azonos az API-val. Az API a forrásból használható felületet írja le; az ABI azt is meghatározza, hogyan találkoznak a lefordított részek. Egy méretállításnál ezért kevés annyi, hogy „64 bites Linux”: az ABI-t és az adatmodellt is meg kell nevezni.
Forráskezeléskor az ISO szabályt szabványszöveggel, a POSIX követelményt POSIX dokumentációval, a Linux API-t Linux dokumentációval, a bővítést fordítódokumentációval kell ellenőrizni. A verzió és a konkrét szakasz lényeges. A helyi mérés csak a rögzített profil megfigyelése, nem általános nyelvi bizonyíték; egy keresési kiindulóoldal pedig önmagában nem bizonyíték.
A könyv a szabály- és státuszállításoknál az alábbi, azonosítóval és változattal megadott dokumentumokra hivatkozik. A szögletes zárójeles rövidítést használjuk a szövegben, a szakaszt pedig a szabványtervezetek stabil szakaszcímkéjével adjuk meg, például [C++20] [dcl.link]. A nyilvános munkatervezet és a kiadott ISO-szöveg között szerkesztési eltérés lehet; a könyv a tervezetet hivatkozási alapként használja, a kiadott szabványt nem idézi szó szerint.
| Rövidítés | Dokumentum | Változat, dátum |
|---|---|---|
| [C17] | ISO/IEC 9899:2018, Programming languages — C | kiadott szabvány, 2018 |
| [C23] | ISO/IEC 9899:2024; nyilvános munkatervezet: WG14 N3096 | kiadott szabvány, 2024; N3096: 2023-04-01 |
| [C++20] | ISO/IEC 14882:2020; nyilvános munkatervezet: WG21 N4861 | kiadott szabvány, 2020; N4861: 2020-04-01 |
| [C++23] | ISO/IEC 14882:2024; nyilvános munkatervezet: WG21 N4950 | kiadott szabvány, 2024; N4950: 2023-05-10 |
| [C++26] | a következő ISO/IEC 14882 kiadás | lásd a státuszt alább |
| [POSIX] | IEEE Std 1003.1-2024 (POSIX.1-2024) | kiadott szabvány, 2024 |
| [psABI] | System V Application Binary Interface, AMD64 Architecture
Processor Supplement,
gitlab.com/x86-psABIs/x86-64-ABI |
a master ág e1ce0983 commitja,
2025-03-12 |
| [GCC] | Using the GNU Compiler Collection, GCC 13.3.0 | a lab-gcc profil fordítója |
| [glibc] | The GNU C Library Reference Manual, glibc 2.39 | a lab-gcc profil C könyvtára |
| [man] | Linux man-pages | 6.7 (Ubuntu 24.04 manpages 6.7-2 csomag) |
A [C++26] státusza 2026-10-25-i ellenőrzés szerint. A WG21 a 2026. márciusi londoni (Croydon) ülésen, 2026. március 28-án lezárta a C++26 technikai munkáját; a beérkezett nemzeti észrevételeket feldolgozta, képességet nem vett ki és nem adott hozzá. A WG21 2026-07-28-i convenori jelentése (N5056) szerint a szabvány DIS-szavazáson van, és a kiadást 2026-ra ütemezik. Az ISO általi kiadást az ellenőrzés napján nem találtuk igazolva, ezért a könyv a C++26-ot technikailag lezárt, még nem kiadott szabványként kezeli. Az implementációs támogatás ettől független; a könyv ezt csak a saját profiljaira méri (lásd a C++26 nyelvi kitekintést).
ISO C17 példa, teljes profile.c fájl;
fordítása a fenti GCC/Clang parancsokkal:
#include <stdio.h>
#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 201710L
#error "C17 or newer is required"
#endif
int main(void)
{
if (printf("__STDC_VERSION__=%ld\n",
__STDC_VERSION__) < 0) {
return 1;
}
return 0;
}C17 módban a kiírandó makróérték 201710; ez elvárt
érték, nem közölt mérés. Az előfeldolgozó ellenőrzése a jelentett nyelvi
szintet vizsgálja, nem az implementáció minden részletét. A kiírás
jelzett hibáját a program nemnulla visszatérési értékkel továbbítja.
A képességvizsgáló makrók (feature-test
macros) ennél szűkebb kérdésre válaszolnak. C++-ban a
__cpp_... jelzők nyelvi, a __cpp_lib_...
jelzők könyvtári képességekhez tartoznak. A könyvtári jelzőket a
megfelelő fejléc vagy a C++20-ban rendelkezésre álló
<version> teszi elérhetővé.
Az alábbi teljes capability.cpp C++20
példa ugyanazon eszközprofilhoz:
#include <version>
#if __cplusplus < 202002L
#error "C++20 or newer is required"
#endif
#if defined(__cpp_lib_int_pow2) && __cpp_lib_int_pow2 >= 202002L
#include <bit>
#endif
constexpr bool is_power_of_two(unsigned int x)
{
#if defined(__cpp_lib_int_pow2) && __cpp_lib_int_pow2 >= 202002L
return std::has_single_bit(x);
#else
return x != 0u && (x & (x - 1u)) == 0u;
#endif
}
static_assert(!is_power_of_two(0u));
static_assert(is_power_of_two(16u));
static_assert(!is_power_of_two(18u));
int main()
{
return 0;
}A fordító először a nyelvi szintet ellenőrzi. Ezután a kiválasztott
standard könyvtár jelzője dönti el, melyik megvalósítás forduljon le. A
static_assert a három megadott esetet fordításkor
ellenőrzi.
Bizonyításvázlat a tartalék megoldáshoz: pozitív,
előjel nélküli
esetén az x - 1u a legalsó egyes bitet nullára, az alatta
levő nullákat egyesre változtatja. Az x & (x - 1u)
tehát eltávolítja a legalsó egyes bitet. Az eredmény pontosan akkor
nulla, ha eredetileg egyetlen egyes bit volt. A külön nullavizsgálat
kizárja a nullát.
A jelző mellett érdemes fordítási és linkelési képességpróbát is végezni. Egy fejléc megléte nem bizonyítja az összes szükséges könyvtári szimbólum elérhetőségét.
ISO fogalom: nem definiált viselkedés (undefined behavior, UB) esetén a szabvány nem támaszt követelményt a viselkedésre.
Hibás C17 részlet: int n = INT_MAX; ++n;, ahol
INT_MAX a <limits.h> szerinti maximum. A
növelés eredménye nem ábrázolható int-ként.
Nem következik belőle garantált körbefordulás, kivétel vagy összeomlás. A látszólag helyes futás sem igazolja a kódot. Javításként a művelet előtt ellenőrizni kell a tartományt.
ISO fogalom: nem specifikált (unspecified) viselkedésnél a szabvány több lehetőséget enged meg, és nem követeli meg a választás dokumentálását.
Például az f() + g() kifejezés nem írja elő, melyik
függvényhívás történjen előbb, ha a kifejezés egyébként szabályos. Ha a
programnak meghatározott sorrend kell, a hívásokat külön, egymást követő
utasításokba kell tenni. Ez nem UB: a választás megengedett
lehetőségekre korlátozott.
ISO fogalom: az implementation-defined viselkedés olyan megengedett választás, amelyet az implementációnak dokumentálnia kell.
Példa a sima char előjelessége. Nem szabad abból
kiindulni, hogy minden targeten ugyanaz. Ha az előjelesség lényeges,
használjunk explicit signed char vagy
unsigned char típust.
Gyakori hiba mindhárom kategóriát „platformfüggőnek” nevezni. Ezzel elvész a döntő különbség: dokumentálandó választásról, megengedett szabadságról vagy garancia nélküli végrehajtásról beszélünk-e.
Magyarázó: sorold réteghez a
printf, epoll, GNU statement expression és
függvényparaméterek regiszterekben történő átadása állításait. Miért
kevés hozzájuk a „C17 program” megjelölés?
Kódolási: egészítsd ki a C++ példát a
1u és 3u bemenetek fordítási idejű
ellenőrzésével. Indokold az elvárt eredményeket.
Diagnosztikai: egy projekt
-std=c++20 mellett lefordul, de nem használható benne egy
kívánt C++20 könyvtári szolgáltatás. Egy másik gépen működik. Milyen
adatokat és próbákat kérnél a hiba besorolásához?
A printf ISO C standard könyvtári felület; az
epoll Linux API; a statement expression fordítóbővítés; a
paraméterátadás ABI-kérdés. Ellenőrzési feltétel:
minden állításhoz megfelelő rétegű dokumentáció legyen rendelhető. A
nyelvi mód nem választ Linux API-t vagy ABI-t.
A meglévő teljes példába illesztendő C++20 részlet:
static_assert(is_power_of_two(1u));
static_assert(!is_power_of_two(3u));Az egyetlen egyes bitet tartalmaz; a bináris alakjában kettő van. Ellenőrzési feltétel: a kiegészített program mindkét fordítóval forduljon le. Ez a példákat ellenőrzi, nem helyettesíti az általános indoklást.
Kérném a teljes fordítási parancsot, a compiler, a standard könyvtár és a target verzióadatait, a releváns feature-test makrót, valamint minimális fordítási és linkelési próbát. Külön vizsgálnám a hiányzó deklarációt és a hiányzó linkelhető megvalósítást. Ellenőrzési feltétel: a két gép közti konkrét eltérés legyen azonosítva; a „másik compiler” önmagában nem diagnózis.
A hordozhatóság kimondott függőségi szerződés. Rögzítsük az explicit nyelvi módot, külön vizsgáljuk a nyelvi és könyvtári támogatást, és minden állítást rendeljünk a megfelelő ISO, POSIX, Linux, ABI vagy fordítóbővítési réteghez. A sikeres fordítás, a dokumentált garancia és a helyi megfigyelés három különböző bizonyíték.