Většina programátorů ví o tom, že některé konstrukce mají tzv. nedefinované chování a že by se jim měli vyhýbat. Méně se ale už ví, že kromě nedefinovaného chování norma definuje další typy chování: "specifikované", implementačně závislé a nespecifikované. Po přečtení tohoto příspěvku byste měli mít jasno, jaký je mezi těmito chováními rozdíl a kterým z nich se vyhýbat.
V rámci jednoduchosti se v následujícím textu budu zabývat pouze jazykem C++ a normou ISO C++98. Pro jazyk C je ale situace obdobná.
Jaké jsou tedy ony typy chování?
Toto chování jsem dal do uvozovek, protože není normou explicitně definované. Patří zde (překvapivě) vše, co je definováno normou a nepatří mezi zbývající tři typy chování.
Příklad:
int b = 24; int main() { int a = b, b; b = 7; }
Norma definuje (sekce 3.3), že v tomto případě se proměnná a
inicializuje hodnotou globální proměnné b
a že se vytvoří lokální proměnná stejného jména (opět b
), jejíž deklarace způsobí to, že přiřazovací příkaz na čtvrtém řádku přiřadí hodnotu 7 do lokální proměnné b
, nikoliv do globální proměnné. Standardní příklad na rozsah platnosti (scope) proměnných.
Definováno v sekci 1.3.5. Konstrukce, které mají implementačně závislé chování, jsou platnými konstrukcemi, ale výsledné chování takovýchto konstrukcí je závislé na překladači. Toto chování ale musí být specifikováno v dokumentaci k danému překladači.
Příklad:
int main() { char c = -1; if (c > 0) { // ... } }
Norma definuje (sekce 3.9.1), že je implementačně závislé, zda datový typ char
je se znaménkem (signed), nebo bez znaménka (unsigned). Takže to, zda se podmínka v příkladu vyhodnotí jako pravdivá či nepravdivá, záleží na překladači.
Definováno v sekci 1.3.13. Toto chování je obdobné implementačně závislému, ale s tím rozdílem, že zvolené chování nemusí být specifikováno v dokumentaci k překladači. Většinou bývá rozsah možností vymezen normou (viz poznámka v sekci 1.3.13).
Příklad:
int main() { int i = 1; i = i++; }
Sekce 5 říká, že pořadí vyhodnocování výrazů mezi sekvenčními body je nespecifikované (pokud nevíte, co to jsou sekvenční body, pak doporučuji tento článek). Tudíž proměnná i
po vyhodnocení příkazu i = i++
může mít nejen hodnotu 2 nebo 3, ale i libovolnou jinou hodnotu (záleží na překladači).
Definováno v sekci 1.3.12 (zajímavé spojení - "norma definuje nedefinované chování" :)). Jak vtipně poznačil dr. Peringer od nás z fakulty, tak pokud má některá konstrukce nedefinované chování, tak vám na hlavu může klidně spadnout měsíc :). Použitím konstrukcí s tímto chováním se program stává neplatným (z hlediska normy). Toto chování mají jak konstrukce, u kterých je to v normě explicitně uvedeno, tak konstrukce, jejichž význam není v normě specifikován. Výsledek takovýchto konstrukcí může být prakticky libovolný - může dojít např. k ignorování situace a ponechání běhu programu "náhodě", ukončení programu (a případnému vytištění chybové zprávy) a další možná chování.
Příklad:
int main() { int *p = 0; *p = 5; }
Nedefinované chování má např. dereference nulového ukazatele (null pointer). Výsledkem bývá často porušení ochrany paměti a násilné ukončení programu operačním systémem, ale rozhodně to není jediné možné chování!
Pokud usilujete o přenositelný kód (což bývá většinou žádané), tak byste se v žádném případě neměli spoléhat na konstrukce, které mají implementačně závislé, nespecifikované či nedefinované chování. Spoléhání na takovéto konstrukce často vede k problémům.
Přidat komentář