1.2 Szabványok, dialektusok és hordozhatósági szerződés

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.

1.2.1 A könyv nyelvi alapprofilja

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-clang

Ezek 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.

1.2.2 Történeti térkép

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.

1.2.3 Az állítások rétegei

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.

1.2.3.1 Forrásjegyzé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).

1.2.4 Nyelvi mód és tényleges képesség

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 xx 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.

1.2.5 Három különböző viselkedési kategória

1.2.5.1 Nem definiált viselkedés

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.

1.2.5.2 Nem specifikált választás

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.

1.2.5.3 Implementáció által meghatározott viselkedés

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.

1.2.6 Feladatok

  1. 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?

  2. 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.

  3. 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?

1.2.7 Megoldások

  1. 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.

  2. 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 1=201=2^0 egyetlen egyes bitet tartalmaz; a 33 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.

  3. 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.

1.2.8 Összefoglaló

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.