Obsah
1. Koncept nedefinovaného chování v jazyku C3
2. Bezpečné a potenciálně nebezpečné režimy překladu
4. Dělení se zbytkem (modulo) nulou
5. Změna znaménka nejmenší celočíselné hodnoty (daného typu) se znaménkem
6. Změna znaménka operací součinu nebo podílu
7. Přetečení u celočíselných typů se znaménkem (signed)
8. Nedefinované hodnoty lokálních konstant v různých režimech překladu
9. Přístup k prvku pole s indexem, který přesahuje meze pole
10. Nepatrně složitější příklad přístupu do neexistujícího prvku pole
11. Překročení meze u řezů (slice)
12. Přístup do paměti přes nulový ukazatel
13. Nedodržení specifikovaného kontraktu: podmínka pro vstupní parametry
14. Nedodržení specifikovaného kontraktu: podmínka pro výslednou hodnotu
15. Přístup k lokální proměnné mimo oblast její platnosti (příklad v C)
16. Realizace stejného programu v jazyku C3
17. Bitový posun se specifikací záporného počtu bitů
19. Repositář s demonstračními příklady
1. Koncept nedefinovaného chování v jazyku C3
Většina článků a především videí o programovacích jazycích se primárně zaměřuje na zajímavé vlastnosti zvoleného programovacího jazyka, popř. na rozdíly mezi popisovaným programovacím jazykem a ostatními mainstreamovými jazyky. Ovšem v praxi je nutné, aby vývojáři dobře znali i temná zákoutí specifikace programovacího jazyka. A mezi tato temná zákoutí patří i nedefinované chování (undefined behavior) neboli krátce UB. Jsou to takové programové konstrukce, které mohou být vykonávány různým způsobem v závislosti na zvoleném překladači, parametrech překladu, použité platformě, operačním systému, zvoleném způsobu zarovnání dat v operační paměti atd. atd. Typicky jsou tyto oblasti ve specifikaci jazyka popsány buď vágním způsobem, nebo je (v lepším případě) přímo specifikováno, že se určitá programová konstrukce může chovat v závislosti na implementaci.
Dnes si popíšeme ty programové konstrukce jazyka C3, u kterých je explicitně řečeno, že mají nedefinované chování (a nejedná se tedy o chyby překladače nebo runtime).
2. Bezpečné a potenciálně nebezpečné režimy překladu
Překladač jazyka C3 může při překladu programů používat různé optimalizační metody. Na rozdíl od (například) programovacího jazyka C je v případě C3 jasně specifikováno, které režimy překladu vedou k takzvanému bezpečnému (safe) kódu a které naopak vedou ke kódu, z něhož byly odstraněny některé bezpečnostní konstrukce (unsafe). Překlad v „bezpečném“ režimu vede k pomalejšímu a objemnějšímu binárnímu kódu, do kterého jsou vloženy testy potenciálně nebezpečných stavů – což od vysokoúrovňového jazyka mnohdy očekáváme. A naopak v unsafe režimu získáme kratší a rychlejší výsledek, který ovšem může vykazovat chybné chování, které není hlášeno (bude ignorováno, pokud kontroly nepřidáme přímo do zdrojových kódů):
-O0 - Safe, no optimizations, emit debug info. -O1 - Safe, high optimization, emit debug info. -O2 - Unsafe, high optimization, emit debug info. -O3 - Unsafe, high optimization, single module, emit debug info. -O4 - Unsafe, highest optimization, relaxed maths, single module, emit debug info, no panic messages. -O5 - Unsafe, highest optimization, fast maths, single module, emit debug info, no panic messages, no backtrace. -Os - Unsafe, high optimization, small code, single module, no debug info, no panic messages. -Oz - Unsafe, high optimization, tiny code, single module, no debug info, no panic messages, no backtrace.
3. Dělení nulou
Velmi dobrým příkladem nedefinovaného chování je dělení nulou. V případě, že jsou zapnuty optimalizace, přesněji řečeno ty optimalizace, které jsou v tabulce z předchozí kapitoly označeny slovem unsafe, může dělení nulou vrátit jakýkoli výsledek. Naopak v safe režimu vznikne při dělení nulou běhová chyba.
Otestovat si to můžeme velmi snadno:
module undefined_behaviour;
import std::io;
fn int div(int a, int b)
{
return a/b;
}
fn void main()
{
io::printf("%d/%d=%d\n", 2, 1, div(2, 1));
io::printf("%d/%d=%d\n", 1, 0, div(1, 0));
}
Překlad s využitím safe a následné spuštění:
$ ./c3c compile-run 01_div_by_zero.c3
V tomto režimu vznikne při pokusu o dělení nulou běhová chyba, která není zachycena, takže je aplikace ukončena:
Program linked to executable './undefined_behaviour'. Launching ./undefined_behaviour 2/1=2 ERROR: 'Division by zero.' in std.core.builtin.default_panic (/home/ptisnovs/xy/c3c/build/lib/std/core/builtin.c3:175) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] in undefined_behaviour.div (/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/01_div_by_zero.c3:6) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] in undefined_behaviour.main (/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/01_div_by_zero.c3:12) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] in @main_to_void_main (/home/ptisnovs/xy/c3c/build/lib/std/core/private/main_stub.c3:18) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] [inline] in main (/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/01_div_by_zero.c3:9) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] in __libc_start_call_main (source unavailable) [/lib64/libc.so.6] in __libc_start_main_alias_2 (source unavailable) [/lib64/libc.so.6] in _start (source unavailable) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] Program interrupted by signal 4.
Naopak překlad s využitím unsafe režimu nebude takovou chybu detekovat a výsledek dělení nulou může být jakýkoli:
$ ./c3c compile-run -O3 01_div_by_zero.c3
Zde konkrétně dělení nulou vrátilo nulovou hodnotu, ale opravdu se může jednat o jakékoli číslo – a chování bude stále přesně odpovídat specifikaci jazyka C3:
Program linked to executable './undefined_behaviour'. Launching ./undefined_behaviour 2/1=2 1/0=0
4. Dělení se zbytkem (modulo) nulou
Naprosto stejným způsobem je jako nedefinované chování označen výsledek výpočtu zbytku po dělení (modulo). Opět si to otestujeme na velmi jednoduchém demonstračním příkladu:
module undefined_behaviour;
import std::io;
fn int mod(int a, int b)
{
return a%b;
}
fn void main()
{
io::printf("%d/%d=%d\n", 2, 1, mod(2, 1));
io::printf("%d/%d=%d\n", 1, 0, mod(1, 0));
}
Překlad v safe režimu
$ ./c3c compile-run 02_mod_by_zero.c3
Opět dojde k vyhození běhové chyby (ovšem odlišné):
Program linked to executable './undefined_behaviour'. Launching ./undefined_behaviour 2/1=0 ERROR: '% by zero.' in std.core.builtin.default_panic (/home/ptisnovs/xy/c3c/build/lib/std/core/builtin.c3:175) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] in undefined_behaviour.mod (/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/02_mod_by_zero.c3:6) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] in undefined_behaviour.main (/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/02_mod_by_zero.c3:12) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] in @main_to_void_main (/home/ptisnovs/xy/c3c/build/lib/std/core/private/main_stub.c3:18) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] [inline] in main (/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/02_mod_by_zero.c3:9) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] in __libc_start_call_main (source unavailable) [/lib64/libc.so.6] in __libc_start_main_alias_2 (source unavailable) [/lib64/libc.so.6] in _start (source unavailable) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] Program interrupted by signal 4.
Naproti tomu v unsafe režimu je chování explicitně nedefinované a může se vrátit jakákoli hodnota:
$ ./c3c compile-run -O3 02_mod_by_zero.c3 Program linked to executable './undefined_behaviour'. Launching ./undefined_behaviour 2/1=0 1/0=0 Program completed with exit code 0.
5. Změna znaménka nejmenší celočíselné hodnoty (daného typu) se znaménkem
V některých programovacích jazycích není definováno, jaký bude výsledek operace pro změnu znaménka v případě, že vstupem do této operace je nejmenší možné celé číslo pro použitý datový typ. Například datový typ short je ve specifikaci programovacího jazyka C3 uveden zcela přesně – jedná se o šestnáctibitovou hodnoty se znaménkem, tedy o hodnoty v rozsahu –32768 až +32767. Problém změny znaménka spočívá v tom, že rozsah hodnot není symetrický okolo nuly, neboli pokud se pokusíme o změnu znaménka hodnoty –32768, bude výsledek mimo rozsah typu short. V případě jazyka C3 je výsledek (na rozdíl od některých jiných jazyků) přesně specifikován:
module undefined_behaviour;
import std::io;
fn short foo(short a)
{
return -a;
}
fn void main()
{
io::printf("%d->%d\n", 0, foo(0));
io::printf("%d->%d\n", 1, foo(1));
io::printf("%d->%d\n", 32767, foo(32767));
io::printf("%d->%d\n", -32767, foo(-32767));
io::printf("%d->%d\n", -32768, foo(-32768));
}
Výsledky jsou v tomto případě vždy přesně specifikovány:
0->0 1->-1 32767->-32767 -32767->32767 -32768->-32768
6. Změna znaménka operací součinu nebo podílu
Prakticky stejné (přesně definované) chování programovací jazyk C3 zajišťuje i v případě součinu nebo podílu hodnotou –1. Opět totiž platí, že C3 i u celočíselných datových typů se znaménkem přesně určuje výsledek -1×x, kde x je nejmenší hodnota daného celočíselného datového typu:
module undefined_behaviour;
import std::io;
fn short foo(short a)
{
return -1*a;
}
fn void main()
{
io::printf("%d->%d\n", 0, foo(0));
io::printf("%d->%d\n", 1, foo(1));
io::printf("%d->%d\n", 32767, foo(32767));
io::printf("%d->%d\n", -32767, foo(-32767));
io::printf("%d->%d\n", -32768, foo(-32768));
}
Výsledky:
0->0 1->-1 32767->-32767 -32767->32767 -32768->-32768
Totéž platí i pro operaci podílu:
module undefined_behaviour;
import std::io;
fn short foo(short a)
{
return a/-1;
}
fn void main()
{
io::printf("%d->%d\n", 0, foo(0));
io::printf("%d->%d\n", 1, foo(1));
io::printf("%d->%d\n", 32767, foo(32767));
io::printf("%d->%d\n", -32767, foo(-32767));
io::printf("%d->%d\n", -32768, foo(-32768));
}
7. Přetečení u celočíselných typů se znaménkem (signed)
Předchozí dva demonstrační příklady ukazovaly (definované) chování programovacího jazyka C3 při provádění operací s hodnotami celočíselného datového typu se znaménkem (signed). Ve skutečnosti jsou v jazyku C3 operace s typy se znaménkem vždy přesně definovány (specifikace říká, že se používá dvojkový doplněk), což je v ostrém kontrastu například s programovacím jazykem C. Například následující demonstrační příklad bude vždy vracet korektní (definované) výsledky, bez ohledu na použitou architekturu či překladač:
module undefined_behaviour;
import std::io;
fn short inc(short a)
{
return a+1;
}
fn void main()
{
io::printf("%d+1=%d\n", 0, inc(0));
io::printf("%d+1=%d\n", 1, inc(1));
io::printf("%d+1=%d\n", 32766, inc(32766));
io::printf("%d+1=%d\n", 32767, inc(32767));
io::printf("%d+1=%d\n", -32767, inc(-32767));
io::printf("%d+1=%d\n", -32768, inc(-32768));
}
Výsledky budou stejné na všech architekturách:
0+1=1 1+1=2 32766+1=32767 32767+1=-32768 -32767+1=-32766 -32768+1=-32767
8. Nedefinované hodnoty lokálních konstant v různých režimech překladu
Popišme si další nedefinované chování programovacího jazyka C3. To se týká lokálních proměnných, tj. proměnných, které jsou viditelné v rámci nějakého bloku. Výchozí hodnota těchto proměnných je určena následujícím způsobem:
- Pokud je provedena explicitní inicializace, má vždy přednost (toto je plně definované chování)
- Neinicializovaná lokální proměnná je vynulována (i zde se jedná o definované chování)
- Proměnná označená jako @noinit (nedefinované chování, závisí na způsobu překladu)
Otestujme všechny tři výše uvedené možnosti na následujícím demonstračním příkladu:
module undefined_behaviour;
import std::io;
fn void main()
{
int x = 0;
int y;
int z @noinit;
io::printf("x: %d\n", x);
io::printf("y: %d\n", y);
io::printf("z: %d\n", z);
}
Překlad v safe režimu může vést k tomu, že se indikuje chyba popř. bude proměnná vynulována (ovšem nevíme, která možnost nastane):
$ c3c compile-run 05_undefined_value.c3 Program linked to executable './undefined_behaviour'. Launching ./undefined_behaviour x: 0 y: 0 z: 0
Naproti tomu překlad v unsafe režimu nás o tomto potenciálním problému vůbec neinformuje a proměnná bude mít nedefinovaný obsah.
9. Přístup k prvku pole s indexem, který přesahuje meze pole
Dalším potenciálně problematickým místem je přístup k prvkům polí nebo řezů v případě, že index prvku, který je čten nebo zapisován, leží mimo meze pole či řezu. Některé (jednodušší) chyby tohoto typu dokáže odhalit překladač jazyka C3. Je tomu tak i v následujícím případě:
module undefined_behaviour;
import std::io;
fn void main()
{
int[10] a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
io::printf("a=%s\n", a);
io::printf("a[0]=%d\n", a[0]);
io::printf("a[9]=%d\n", a[9]);
io::printf("a[10]=%d\n", a[10]);
}
Chyba na posledním řádku funkce main je v tomto případě překladačem jazyka C3 odhalena:
$ ./c3c compile-run 06_out_of_bounds_1.c3
9:
10: io::printf("a[0]=%d\n", a[0]);
11: io::printf("a[9]=%d\n", a[9]);
12: io::printf("a[10]=%d\n", a[10]);
^^
(/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/06_out_of_bounds_1.c3:12:32) Error: An index of '10' is out of range, a value between 0 and 9 was expected.
10. Nepatrně složitější příklad přístupu do neexistujícího prvku pole
V předchozím demonstračním příkladu sice překladač programovacího jazyka C3 korektně odhalil přístup k neexistujícímu prvku pole, ovšem další (podobné) případy již odhalit nedokáže (alespoň nikoli v současné verzi). Například v následujícím zdrojovém kódu modifikujeme v programové smyčce všechny prvky pole, ovšem meze smyčky jsou nastaveny špatně – počítáme s jedním prvkem navíc:
module undefined_behaviour;
import std::io;
fn void main()
{
int[10] a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
io::printf("a=%s\n", a);
for (int i=0; i<a.len+1; i++) {
io::printf("a[%d]=%d\n", i, a[i]);
a[i] = (i + 1)*10;
}
}
Tato chyba je detekována až po spuštění programu, ovšem pouze v případě, že provedeme překlad v safe režimu:
$ ./c3c compile-run 07_out_of_bounds_2.c3 Program linked to executable './undefined_behaviour'. Launching ./undefined_behaviour a=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] a[0]=1 a[1]=2 a[2]=3 a[3]=4 a[4]=5 a[5]=6 a[6]=7 a[7]=8 a[8]=9 a[9]=10 ERROR: 'Array index out of bounds (array had size 10, index was 10)' in std.core.builtin.default_panic (/home/ptisnovs/xy/c3c/build/lib/std/core/builtin.c3:175) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] in std.core.builtin.panicf (/home/ptisnovs/xy/c3c/build/lib/std/core/builtin.c3:231) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] in undefined_behaviour.main (/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/07_out_of_bounds_2.c3:11) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] in @main_to_void_main (/home/ptisnovs/xy/c3c/build/lib/std/core/private/main_stub.c3:18) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] [inline] in main (/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/07_out_of_bounds_2.c3:4) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] in __libc_start_call_main (source unavailable) [/lib64/libc.so.6] in __libc_start_main_alias_2 (source unavailable) [/lib64/libc.so.6] in _start (source unavailable) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] Program interrupted by signal 4.
Po překladu v režimu unsafe přistupujeme k neexistujícímu prvku a tedy do paměti, která nám nepatří:
$ ./c3c compile-run -O5 07_out_of_bounds_2.c3 Program linked to executable './undefined_behaviour'. Launching ./undefined_behaviour a=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] a[0]=1 a[1]=2 a[2]=3 a[3]=4 a[4]=5 a[5]=6 a[6]=7 a[7]=8 a[8]=9 a[9]=10 a[10]=0 Program completed with exit code 0.
11. Překročení meze u řezů (slice)
Podobně může program reagovat (v runtime, tedy za běhu) i při přístupu do pole přes řez (slice). Pokusme se tedy program z předchozí kapitoly upravit takovým způsobem, aby se namísto pole použil řez:
module undefined_behaviour;
import std::io;
fn void main()
{
io::printf("type: %s\n", int[].nameof);
int[] a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
io::printf("a=%s\n", a);
for (int i=0; i<a.len+1; i++) {
a[i] = (i + 1)*10;
}
io::printf("a=%s\n", a);
}
Pokud je překlad proveden v safe režimu, program po svém spuštění detekuje přístup mimo meze řezu a bude nás o tom patřičně informovat:
$ c3c compile-run 11_out_of_bounds_slice.c3 Program linked to executable './undefined_behaviour'. Launching ./undefined_behaviour type: int[] a=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ERROR: 'Array index out of bounds (array had size 10, index was 10)' in std.core.builtin.default_panic (/home/ptisnovs/xy/c3c/build/lib/std/core/builtin.c3:175) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] in std.core.builtin.panicf (/home/ptisnovs/xy/c3c/build/lib/std/core/builtin.c3:231) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] in undefined_behaviour.main (/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/11_out_of_bounds_slice.c3:13) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] in @main_to_void_main (/home/ptisnovs/xy/c3c/build/lib/std/core/private/main_stub.c3:18) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] [inline] in main (/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/11_out_of_bounds_slice.c3:4) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] in __libc_start_call_main (source unavailable) [/lib64/libc.so.6] in __libc_start_main_alias_2 (source unavailable) [/lib64/libc.so.6] in _start (source unavailable) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] Program interrupted by signal 4.
V unsafe režimu však kontroly ve výsledném spustitelném souboru chybí a opět tedy přistupujeme k paměti, která nám nepatří (a stát se může obecně cokoli):
$ ./c3c compile-run -O5 11_out_of_bounds_slice.c3 Program linked to executable './undefined_behaviour'. Launching ./undefined_behaviour type: int[] a=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] a=[10, 20, 30, 40, 50, 60, 70, 80, 90, 100] Program completed with exit code 0.
12. Přístup do paměti přes nulový ukazatel
Na další nedefinované chování narazíme ve chvíli, kdy běžící program přistupuje k paměti přes nulový (null) ukazatel. Podívejme se na ten nejjednodušší případ takového chování (který kupodivu není odhalen překladačem). Pokoušíme se vypsat hodnotu z paměti, ovšem namísto korektní adresy se předává nulový ukazatel:
module undefined_behaviour;
import std::io;
fn void main()
{
int *p = null;
io::printf("%d\n", *p);
}
Pokud je program přeložen v safe režimu, bude chyba odhalena, ale až v runtime:
$ c3c compile-run 08_null_pointer.c3 Program linked to executable './undefined_behaviour'. Launching ./undefined_behaviour ERROR: 'Dereference of null pointer, '(int*)arg' was null.' in std.core.builtin.default_panic (/home/ptisnovs/xy/c3c/build/lib/std/core/builtin.c3:175) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] in std.io.int_from_any (/home/ptisnovs/xy/c3c/build/lib/std/io/formatter_private.c3:64) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] in @wrap_bad (/home/ptisnovs/xy/c3c/build/lib/std/io/formatter.c3:558) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] [inline] in std.io.Formatter.vprintf (/home/ptisnovs/xy/c3c/build/lib/std/io/formatter.c3:558) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] in std.io.printf (/home/ptisnovs/xy/c3c/build/lib/std/io/io.c3:299) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] in undefined_behaviour.main (/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/08_null_pointer.c3:8) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] in @main_to_void_main (/home/ptisnovs/xy/c3c/build/lib/std/core/private/main_stub.c3:18) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] [inline] in main (/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/08_null_pointer.c3:4) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] in __libc_start_call_main (source unavailable) [/lib64/libc.so.6] in __libc_start_main_alias_2 (source unavailable) [/lib64/libc.so.6] in _start (source unavailable) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] Program interrupted by signal 4.
Naopak v unsafe režimu runtime systém jazyka C3 problém neodhalí a program může zhavarovat až při realizaci přístupu na nulovou adresu (což není v chráněném režimu povoleno):
$ ./c3c compile-run -O5 08_null_pointer.c3 Program linked to executable './undefined_behaviour'. Launching ./undefined_behaviour Program interrupted by signal 11.
13. Nedodržení specifikovaného kontraktu: podmínka pro vstupní parametry
Připomeňme si, že v jazyku C3 je možné u funkcí a metod specifikovat takzvané kontrakty, což jsou podmínky, které musí platit pro vstupní parametry funkce (platné a inicializované v době jejího volání) popř. naopak podmínky, které musí platit pro výslednou hodnotu. Podpora kontraktů je mimochodem jedním z nejužitečnějších rysů tohoto programovacího jazyka. Ovšem kontrakty mají též obecně nedefinované chování. Jsou totiž vyhodnocovány pouze při použití safe režimu překladu.
Otestujme si to na kontraktu definovaném pro hodnotu vstupního parametru. Pokusíme se zabránit dělení nulou:
module undefined_behaviour;
import std::io;
<*
@require b != 0
*>
fn int div(int a, int b)
{
return a/b;
}
fn void main()
{
io::printf("%d/%d=%d\n", 2, 1, div(2, 1));
io::printf("%d/%d=%d\n", 1, 0, div(1, 0));
}
Tento kontrakt, resp. jeho porušení může být detekováno, ovšem není zaručeno, že to bude platit i pro unsafe režimy překladu:
$ c3c compile-run 09_contract.c3
12: fn void main()
13: {
14: io::printf("%d/%d=%d\n", 2, 1, div(2, 1));
15: io::printf("%d/%d=%d\n", 1, 0, div(1, 0));
^^^^^^^^^
(/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/09_contract.c3:15:36) Error: @require "b != 0" violated.
Naopak překlad v unsafe režimu povede k tomu, že se bude dělit nulou, což je vlastně druhé nedefinované chování způsobené prvním nedefinovaným chováním:
$ c3c compile-run -O5 09_contract.c3 Program linked to executable './undefined_behaviour'. Launching ./undefined_behaviour 2/1=2 1/0=0 Program completed with exit code 0.
14. Nedodržení specifikovaného kontraktu: podmínka pro výslednou hodnotu
Prakticky totéž platí i pro kontrakt, kterým je určena podmínka či podmínky, které musí platit pro výslednou (návratovou) hodnotu funkce. Opět si to otestujme:
module undefined_behaviour;
import std::io;
<*
@ensure return > a
*>
fn short add(short a, short b)
{
return a+b;
}
fn void main()
{
io::printf("%d+%d=%d\n", 1, 1, add(1, 1));
io::printf("%d+%d=%d\n", 32767, 1, add(32767, 1));
}
Chování tohoto programu se bude opět odlišovat v závislosti na tom, jaký režim překladu je použit.
Použití safe režimu překladu:
$ ./c3c compile-run 10_contract.c3 Program linked to executable './undefined_behaviour'. Launching ./undefined_behaviour 1+1=2 ERROR: '@ensure "return > a" violated.' in std.core.builtin.default_panic (/home/ptisnovs/xy/c3c/build/lib/std/core/builtin.c3:175) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] in undefined_behaviour.add (/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/10_contract.c3:9) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] in undefined_behaviour.main (/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/10_contract.c3:15) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] in @main_to_void_main (/home/ptisnovs/xy/c3c/build/lib/std/core/private/main_stub.c3:18) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] [inline] in main (/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/10_contract.c3:12) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] in __libc_start_call_main (source unavailable) [/lib64/libc.so.6] in __libc_start_main_alias_2 (source unavailable) [/lib64/libc.so.6] in _start (source unavailable) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour]
Použití unsafe režimu překladu:
$ ./c3c compile-run -O5 10_contract.c3 Program linked to executable './undefined_behaviour'. Launching ./undefined_behaviour 1+1=2 Program interrupted by signal 11.
15. Přístup k lokální proměnné mimo oblast její platnosti (příklad v C)
V této kapitole na chvíli odbočme od jazyka C3 zpátky k jeho ideovému předchůdci, tedy ke slavnému céčku. Podívejme se na následující příklad, ve kterém se z funkce foo vrací ukazatel na lokální proměnnou. Problém spočívá v tom, že mimo čas, kdy se provádí příkazy ve funkci foo, nejsou lokální (nestatické) proměnné alokovány, takže nemá smysl vracet odkaz na ně (a už vůbec ne přistupovat přes tento odkaz do paměti):
#include <stdio.h>
int* foo()
{
int a = 42;
return &a;
}
int main(void)
{
int *p = foo();
printf("%d\n", *p);
*p = -1;
return 0;
}
Běžný překlad pomocí GCC i jiných překladačů bude proveden, „pouze“ se vypíše varování:
$ gcc -Wall -pedantic 12_local_variable_pointer.c
12_local_variable_pointer.c: In function ‘foo’:
12_local_variable_pointer.c:6:12: warning: function returns address of local variable [-Wreturn-local-addr]
6 | return &a;
| ^~
popř.:
$ clang 12_local_variable_pointer.c
12_local_variable_pointer.c:6:13: warning: address of stack memory associated with local variable 'a' returned [-Wreturn-stack-address]
6 | return &a;
| ^
1 warning generated.
Chyba (resp. přímo pád) nastane v runtime:
$ ./a.out Segmentation fault (core dumped)
Aby byla chyba ohlášena jako skutečná chyba, musíme překladači předat přepínač -Werror:
$ gcc -Wall -Werror -pedantic 12_local_variable_pointer.c
12_local_variable_pointer.c: In function ‘foo’:
12_local_variable_pointer.c:6:12: error: function returns address of local variable [-Werror=return-local-addr]
6 | return &a;
| ^~
cc1: all warnings being treated as errors
popř.:
$ clang -Werror 12_local_variable_pointer.c
12_local_variable_pointer.c:6:13: error: address of stack memory associated with local variable 'a' returned [-Werror,-Wreturn-stack-address]
6 | return &a;
| ^
1 error generated.
16. Realizace stejného programu v jazyku C3
Pokud naprosto stejný program přepíšeme do jazyka C3, bude přístup k lokální proměnné vně funkce korektně detekován překladačem, což je ostatně jen dobře:
module undefined_behaviour;
import std::io;
fn int* foo()
{
int a = 42;
return &a;
}
fn void main()
{
int *p = foo();
io::printf("%d\n", *p);
}
Chyba nahlášená při pokusu o překlad (nezávisle na použité úrovni optimalizace):
$ ./c3c compile-run 12_local_variable_pointer.c3
4: fn int* foo()
5: {
6: int a = 42;
7: return &a;
^^
(/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/12_local_variable_pointer.c3:7:12) Error: A pointer to a local variable will be invalid once the function returns. Allocate the data on the heap or temp memory to return a pointer.
17. Bitový posun se specifikací záporného počtu bitů
Dalším typicky nedefinovaným chováním je v jazyku C3 (ale i v C) bitový posun o záporný počet bitů (to může být poněkud překvapující, ale posledních minimálně čtyřicet let tomu tak skutečně je):
module undefined_behaviour;
import std::io;
fn short shift(short a, short b)
{
return a<<b;
}
fn void main()
{
io::printf("%d+1=%d\n", 0, shift(0, -1));
io::printf("%d+1=%d\n", 1, shift(1, -1));
io::printf("%d+1=%d\n", 2, shift(2, -1));
io::printf("%d+1=%d\n", 32766, shift(32766, -1));
io::printf("%d+1=%d\n", 32767, shift(32767, -1));
io::printf("%d+1=%d\n", -32767, shift(-32767, -1));
io::printf("%d+1=%d\n", -32768, shift(-32768, -1));
}
Chování se opět může odlišovat podle toho, zda je překlad proveden v safe nebo unsafe režimu.
Použití safe režimu:
ERROR: 'Shift amount out of range (was 65535).' in std.core.builtin.default_panic (/home/ptisnovs/xy/c3c/build/lib/std/core/builtin.c3:175) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] in std.core.builtin.panicf (/home/ptisnovs/xy/c3c/build/lib/std/core/builtin.c3:231) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] in undefined_behaviour.shift (/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/15_negative_shift.c3:6) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] in undefined_behaviour.main (/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/15_negative_shift.c3:11) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] in @main_to_void_main (/home/ptisnovs/xy/c3c/build/lib/std/core/private/main_stub.c3:18) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] [inline] in main (/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/15_negative_shift.c3:9) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] in __libc_start_call_main (source unavailable) [/lib64/libc.so.6] in __libc_start_main_alias_2 (source unavailable) [/lib64/libc.so.6] in _start (source unavailable) [/home/ptisnovs/src/c3-examples/c3-undefined-behaviour/undefined_behaviour] Program interrupted by signal 4.
Použití unsafe režimu (může nastat cokoli):
0+1=0 1+1=0 2+1=0 32766+1=0 32767+1=0 -32767+1=0 -32768+1=0
18. Obsah navazující části
Ve skutečnosti je v jazyku C3 nedefinovaných chování větší množství. Zbylé z nich si popíšeme v navazujícím článku.
19. Repositář s demonstračními příklady
Demonstrační příklady vytvořené pro nejnovější verzi programovacího jazyka C3 byly uloženy do repositáře dostupného na adrese https://github.com/tisnik/c3-examples. Následují odkazy na jednotlivé příklady (či jejich nedokončené části).
Demonstrační příklady z prvního článku o programovacím jazyku C3:
| # | Příklad | Stručný popis | Adresa |
|---|---|---|---|
| 1 | factorial.c3 | realizace výpočtu faktoriálu | https://github.com/tisnik/c3-examples/blob/master/introduction/factorial.c3 |
| 2 | factorial_macro.c3 | výpočet faktoriálu konkrétní hodnoty implementovaný formou makra | https://github.com/tisnik/c3-examples/blob/master/introduction/factorial_macro.c3 |
| 3 | swap_macro.c3 | makro realizující prohození dvou hodnot | https://github.com/tisnik/c3-examples/blob/master/introduction/swap_macro.c3 |
| 4 | renderer.c | výpočet a vykreslení Juliovy množiny implementovaný v jazyku C | https://github.com/tisnik/c3-examples/blob/master/introduction/renderer.c |
| 5 | renderer_v1.c3 | definice datové struktury s rozměry rastrového obrázku a skeleton všech funkcí | https://github.com/tisnik/c3-examples/blob/master/introduction/renderer_v1.c3 |
| 6 | renderer_v2.c3 | anotace parametrů funkcí typu ukazatel (pointer) | https://github.com/tisnik/c3-examples/blob/master/introduction/renderer_v2.c3 |
| 7 | renderer_v3.c3 | statická kontrola, zda se nepředávají neinicializované ukazatele | https://github.com/tisnik/c3-examples/blob/master/introduction/renderer_v3.c3 |
| 8 | renderer_v4.c3 | runtime kontrola, zda se nepředávají neinicializované ukazatele | https://github.com/tisnik/c3-examples/blob/master/introduction/renderer_v4.c3 |
| 9 | renderer_v5.c3 | první (nekorektní) varianta funkce pro inicializaci barvové palety | https://github.com/tisnik/c3-examples/blob/master/introduction/renderer_v5.c3 |
| 10 | renderer_v6.c3 | druhá (korektní) varianta funkce pro inicializaci barvové palety | https://github.com/tisnik/c3-examples/blob/master/introduction/renderer_v6.c3 |
| 11 | renderer_v7.c3 | volání knihovní I/O funkce a volání nativní céčkovské funkce | https://github.com/tisnik/c3-examples/blob/master/introduction/renderer_v7.c3 |
| 12 | renderer_v8.c3 | plně funkční program pro výpočet a vykreslení Juliovy množiny | https://github.com/tisnik/c3-examples/blob/master/introduction/renderer_v8.c3 |
Demonstrační příklady ze druhého článku o jazyku C3:
| # | Příklad | Stručný popis | Adresa |
|---|---|---|---|
| 13 | 01_just_main.c3 | struktura nejjednoduššího programu obsahujícího pouze prázdnou funkci main | https://github.com/tisnik/c3-examples/blob/master/c3-basics/01_just_main.c3 |
| 14 | 02_module_name.c3 | struktura programu s uvedeným plným jménem modulu | https://github.com/tisnik/c3-examples/blob/master/c3-basics/02_module_name.c3 |
| 15 | 03_hello_world.c3 | klasický program typu „Hello, world!“ napsaný v jazyku C3 | https://github.com/tisnik/c3-examples/blob/master/c3-basics/03_hello_world.c3 |
| 16 | 04_exit_value.c3 | ukončení procesu s předáním návratového kódu zpět volajícímu programu | https://github.com/tisnik/c3-examples/blob/master/c3-basics/04_exit_value.c3 |
| 17 | 05_c_function.c3 | zavolání funkce definované v knihovně programovacího jazyka C | https://github.com/tisnik/c3-examples/blob/master/c3-basics/05_c_function.c3 |
| 18 | 06_bool_type.c3 | definice proměnných typu pravdivostní hodnota (bool) | https://github.com/tisnik/c3-examples/blob/master/c3-basics/06_bool_type.c3 |
| 19 | 07_int_to_bool.c3 | implicitní převod hodnoty typu int na pravdivostní hodnotu (nekorektní řešení) | https://github.com/tisnik/c3-examples/blob/master/c3-basics/07_int_to_bool.c3 |
| 20 | 08_int_to_bool.c3 | explicitní převod hodnoty typu int na pravdivostní hodnotu (korektní řešení) | https://github.com/tisnik/c3-examples/blob/master/c3-basics/08_int_to_bool.c3 |
| 21 | 09_int_to_bool.c3 | explicitní převod hodnoty typu int na pravdivostní hodnotu (nekorektní řešení) | https://github.com/tisnik/c3-examples/blob/master/c3-basics/09_int_to_bool.c3 |
| 22 | 10_bool_sizeof.c3 | zjištění velikosti paměti obsazené hodnotou typu bool | https://github.com/tisnik/c3-examples/blob/master/c3-basics/10_bool_sizeof.c3 |
| 23 | 11_int_types.c3 | definice proměnných typu celé číslo se znaménkem s různou bitovou šířkou | https://github.com/tisnik/c3-examples/blob/master/c3-basics/11_int_types.c3 |
| 24 | 12_uint_types.c3 | definice proměnných typu celé číslo bez znaménka s různou bitovou šířkou | https://github.com/tisnik/c3-examples/blob/master/c3-basics/12_uint_types.c3 |
| 25 | 13_no_suffixes.c3 | celočíselné konstanty bez uvedení suffixu (bitové šířky) | https://github.com/tisnik/c3-examples/blob/master/c3-basics/13_no_suffixes.c3 |
| 26 | 14_suffixes.c3 | celočíselné konstanty s uvedením sufficu (bitové šířky) | https://github.com/tisnik/c3-examples/blob/master/c3-basics/14_suffixes.c3 |
| 27 | 15_int_sizeof.c3 | zjištění velikosti paměti obsazené celočíselnými hodnotami se znaménkem | https://github.com/tisnik/c3-examples/blob/master/c3-basics/15_int_sizeof.c3 |
| 28 | 16_uint_sizeof.c3 | zjištění velikosti paměti obsazené celočíselnými hodnotami bez znaménka | https://github.com/tisnik/c3-examples/blob/master/c3-basics/16_uint_sizeof.c3 |
| 29 | 17_int_conversions.c3 | korektní převody mezi celočíselnými hodnotami s různou bitovou šířkou | https://github.com/tisnik/c3-examples/blob/master/c3-basics/17_int_conversions.c3 |
| 30 | 18_int_conversions.c3 | nekorektní převody mezi celočíselnými hodnotami s různou bitovou šířkou | https://github.com/tisnik/c3-examples/blob/master/c3-basics/18_int_conversions.c3 |
| 31 | 19_int_conversions.c3 | explicitní převody a přetečení hodnot | https://github.com/tisnik/c3-examples/blob/master/c3-basics/19_int_conversions.c3 |
| 32 | 20_float_types.c3 | definice proměnných typu numerická hodnota s plovoucí řádovou čárkou (tečkou) | https://github.com/tisnik/c3-examples/blob/master/c3-basics/20_float_types.c3 |
| 33 | 21_vector_type.c3 | definice vektoru obsahujícího celočíselné hodnoty | https://github.com/tisnik/c3-examples/blob/master/c3-basics/21_vector_type.c3 |
| 34 | 22_vector_operations.c3 | základní operace s celými vektory | https://github.com/tisnik/c3-examples/blob/master/c3-basics/22_vector_operations.c3 |
| 35 | 23_vector_sizes.c3 | zjištění a tisk velikosti vektorů (různé datové typy prvků vektorů, shodná délka) | https://github.com/tisnik/c3-examples/blob/master/c3-basics/23_vector_sizes.c3 |
| 36 | 24_vector_sizes.c3 | zjištění a tisk velikosti vektorů (stejné datové typy prvků vektorů, odlišná délka) | https://github.com/tisnik/c3-examples/blob/master/c3-basics/24_vector_sizes.c3 |
Demonstrační příklady použité ve třetím článku o jazyku C3:
Demonstrační příklady ze čtvrtého o jazyku C3:
| # | Příklad | Stručný popis | Adresa |
|---|---|---|---|
| 57 | 01_program_stub.c3 | struktura programu s uvedeným plným jménem modulu | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/01_program_stub.c3 |
| 58 | 02_if.c3 | nejjednodušší forma rozvětvení založené na konstrukci if | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/02_if.c3 |
| 59 | 03_if_else.c3 | plné rozvětvení realizované konstrukcí if-else | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/03_if_else.c3 |
| 60 | 04_improper_if.c3 | nekorektní způsob zápisu programové konstrukce if-else (porovnání s jazykem C) | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/04_improper_if.c3 |
| 61 | 05_improper_if.c3 | nekorektní způsob zápisu programové konstrukce if-else (porovnání s jazykem C) | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/05_improper_if.c3 |
| 62 | 06_if_else_if.c3 | složitější rozvětvení založené na programové konstrukci if-else if-else | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/06_if_else_if.c3 |
| 63 | 07_switch_basic.c3 | základní forma vícenásobného rozvětvení založeného na konstrukci switch-case | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/07_switch_basic.c3 |
| 64 | 08_switch_basic.c3 | větší množství podmínek a programová konstrukce switch-case | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/08_switch_basic.c3 |
| 65 | 09_switch_condition.c3 | podmínky zapsané ve větvích programové konstrukci switch-case vyhodnocované v čase běhu procesu | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/09_switch_condition.c3 |
| 66 | 10_switch_true.c3 | konstrukce switch-case bez uvedeného výrazu za klíčovým slovem switch | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/10_switch_true.c3 |
| 67 | 11_switch_break.c3 | zápis prázdné větve default v programové konstrukci switch-case | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/11_switch_break.c3 |
| 68 | 12_switch_nextcase.c3 | pokračování ve vykonávání konstrukce switch-case vynucené klíčovým slovem nextcase | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/12_switch_nextcase.c3 |
| 69 | 13_for_loop.c3 | základní forma programové smyčky realizované klíčovým slovem for | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/13_for_loop.c3 |
| 70 | 14_foreach_loop.c3 | základní forma programové smyčky typu for-each | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/14_foreach_loop.c3 |
| 71 | 15_foreach_loop.c3 | programová smyčka for-each vracející index prvku i hodnotu prvku | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/15_foreach_loop.c3 |
| 72 | 16_foreach_loop.c3 | modifikace obsahu pole v programové smyčce for-each | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/16_foreach_loop.c3 |
| 73 | 17_foreach_loop.c3 | pokus o modifikaci obsahu procházeného pole | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/17_foreach_loop.c3 |
| 74 | 18_foreach_loop.c3 | modifikace procházeného pole přes ukazatel na prvek | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/18_foreach_loop.c3 |
| 75 | 19_foreach_r_loop.c3 | programová smyčka for-each, ve které se sekvencí prochází v opačném pořadí | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/19_foreach_r_loop.c3 |
| 76 | 20_while_loop.c3 | základní forma programové smyčky typu while | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/20_while_loop.c3 |
| 77 | 21_while_loop2.c3 | programová smyčka typu while s konstrukcí break | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/21_while_loop2.c3 |
| 78 | 22_nested_loops.c3 | realizace vnořených programových smyček | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/22_nested_loops.c3 |
| 79 | 23_break.c3 | vnořené programové smyčky a příkaz break: ukončení vnitřní smyčky | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/23_break.c3 |
| 80 | 24_break.c3 | vnořené programové smyčky a příkaz break: ukončení vnější smyčky | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/24_break.c3 |
| 81 | 25_break.c3 | vnořené programové smyčky a příkaz break, varianta se smyčkami typu while | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/25_break.c3 |
Demonstrační příklady z pátého článku o jazyku C3:
Následují odkazy na demonstrační příklady z článku předchozího:
| # | Příklad | Stručný popis | Adresa | |
|---|---|---|---|---|
| 100 | 01_regular_function.c3 | deklarace běžných funkcí v jazyku C3 | https://github.com/tisnik/c3-examples/blob/master/c3-functions/01_regular_function.c3 | |
| 101 | 02_check_arguments.c3 | kontrola parametrů předávaných do funkce překladačem jazyka C3 | https://github.com/tisnik/c3-examples/blob/master/c3-functions/02_check_arguments.c3 | |
| 102 | 03_default_arguments.c3 | funkce s jedním parametrem, který má nastavenou výchozí hodnotu a jedním běžným parametrem (korektní pořadí parametrů) | https://github.com/tisnik/c3-examples/blob/master/c3-functions/03_default_arguments.c3 | |
| 103 | 04_default_arguments.c3 | funkce se všemi parametry s nastavenu výchozí hodnotou | https://github.com/tisnik/c3-examples/blob/master/c3-functions/04_default_arguments.c3 | |
| 104 | 05_default_arguments.c3 | funkce s jedním parametrem, který má nastavenou výchozí hodnotu a jedním běžným parametrem (nekorektní pořadí parametrů) | https://github.com/tisnik/c3-examples/blob/master/c3-functions/05_default_arguments.c3 | |
| 105 | 06_named_arguments.c3 | explicitní uvedení jmen parametrů při volání funkce | https://github.com/tisnik/c3-examples/blob/master/c3-functions/06_named_arguments.c3 | |
| 106 | 07_named_arguments.c3 | explicitní uvedení jmen parametrů při volání funkce | https://github.com/tisnik/c3-examples/blob/master/c3-functions/07_named_arguments.c3 | |
| 107 | 08_named_default_arguments.c3 | pojmenování parametrů s výchozí hodnotou při volání funkce | https://github.com/tisnik/c3-examples/blob/master/c3-functions/08_named_default_arguments.c3 | |
| 108 | 09_sum.c3 | realizace funkce pro výpočet součtu všech předaných hodnot | https://github.com/tisnik/c3-examples/blob/master/c3-functions/09_sum.c3 | |
| 109 | 10_sum.c3 | předání obsahu pole do funkce s proměnným počtem parametrů | https://github.com/tisnik/c3-examples/blob/master/c3-functions/10_sum.c3 | |
| 110 | 11_varargs.c3 | pořadí předávání parametrů do funkce s proměnným počtem parametrů (nekorektní způsob použití) | https://github.com/tisnik/c3-examples/blob/master/c3-functions/11_varargs.c3 | |
| 111 | 12_varargs.c3 | pořadí předávání parametrů do funkce s proměnným počtem parametrů (korektní způsob použití) | https://github.com/tisnik/c3-examples/blob/master/c3-functions/12_varargs.c3 | |
| 112 | 13_optional.c3 | funkce vracející hodnotu typu Optional | https://github.com/tisnik/c3-examples/blob/master/c3-functions/13_optional.c3 | |
| 113 | 14_optional.c3 | využití operátoru ?? | https://github.com/tisnik/c3-examples/blob/master/c3-functions/14_optional.c3 | |
| 114 | 15_contract.c3 | kontrakty uvedené u funkcí | https://github.com/tisnik/c3-examples/blob/master/c3-functions/15_contract.c3 | |
| 115 | 16_contract.c3 | kontrakty uvedené u funkcí | https://github.com/tisnik/c3-examples/blob/master/c3-functions/16_contract.c3 | |
| 116 | 17_c_declaration.c3 | deklarace funkce bez parametrů „céčkovým způsobem“ (nekorektní zápis) | https://github.com/tisnik/c3-examples/blob/master/c3-functions/17_c_declaration.c3 | |
| 117 | 18_check_return_type.c3 | kontrola návratové hodnoty překladačem jazyka C3 | https://github.com/tisnik/c3-examples/blob/master/c3-functions/18_check_return_type.c3 | |
| 118 | 19_check_return_value.c3 | kontrola počtu návratových hodnot překladačem jazyka C3 | https://github.com/tisnik/c3-examples/blob/master/c3-functions/19_check_return_value.c3 | |
| 119 | 20_in_out_params.c3 | předání ukazatelů do volané funkce | https://github.com/tisnik/c3-examples/blob/master/c3-functions/20_in_out_params.c3 | |
| 120 | 21_in_out_params.c3 | označení ukazatelů kontrakty [in] a [out] | https://github.com/tisnik/c3-examples/blob/master/c3-functions/21_in_out_params.c3 | |
| 121 | 22_in_out_params.c3 | označení ukazatelů kontrakty [in] a [out] | https://github.com/tisnik/c3-examples/blob/master/c3-functions/22_in_out_params.c3 | |
| 122 | 23_void_pointer.c3 | předávání ukazatelů typu void * do volané funkce | https://github.com/tisnik/c3-examples/blob/master/c3-functions/23_void_pointer.c3 | |
| 123 | 24_contract.c3 | kontrakt zapsaný před hlavičkou funkce | https://github.com/tisnik/c3-examples/blob/master/c3-functions/24_contract.c4 |
Demonstrační příklady z předchozího článku o funkcích v C3:
Příklady z předchozího článku s popisem makrosystému jazyka C3:
| # | Příklad | Stručný popis | Adresa |
|---|---|---|---|
| 145 | 01_add_macro.c3 | jednoduché makro, které může být expandováno v rámci výrazu | https://github.com/tisnik/c3-examples/blob/master/c3-macros/01_add_macro.c3 |
| 146 | 02_add_macro.c3 | makro je generické, lze použít s hodnotami různých typů | https://github.com/tisnik/c3-examples/blob/master/c3-macros/02_add_macro.c3 |
| 147 | 03_macro_expansion1.c | expanze makro provedená preprocesorem jazyka C (parametry nejsou uzávorkovány) | https://github.com/tisnik/c3-examples/blob/master/c3-macros/03_macro_expansion1.c |
| 148 | 03_macro_expansion2.c | expanze makro provedená preprocesorem jazyka C (parametry jsou uzávorkovány) | https://github.com/tisnik/c3-examples/blob/master/c3-macros/03_macro_expansion2.c |
| 149 | 03_macro_expansion.c3 | řešení v jazyku C3 bez nutnosti uzávorkování parametrů makra | https://github.com/tisnik/c3-examples/blob/master/c3-macros/03_macro_expansion.c3 |
| 150 | 04_macro_expansion1.c | expanze makro provedená preprocesorem jazyka C (tělo makra není v závorkách) | https://github.com/tisnik/c3-examples/blob/master/c3-macros/04_macro_expansion1.c |
| 151 | 04_macro_expansion2.c | expanze makro provedená preprocesorem jazyka C (tělo makra je v závorkách) | https://github.com/tisnik/c3-examples/blob/master/c3-macros/04_macro_expansion2.c |
| 152 | 04_macro_expansion.c3 | řešení v jazyku C3 bez nutnosti zápisu těla makra do závorek | https://github.com/tisnik/c3-examples/blob/master/c3-macros/04_macro_expansion.c3 |
| 153 | 05_typed_macro.c3 | makro s definicí typů parametrů | https://github.com/tisnik/c3-examples/blob/master/c3-macros/05_typed_macro.c3 |
| 154 | 06_typed_macro.c3 | makro s definicí typů parametrů: příklad jeho expanze | https://github.com/tisnik/c3-examples/blob/master/c3-macros/06_typed_macro.c3 |
| 155 | 07_compile_time1.c3 | expanze makra v čase překladu: nefunkční varianta s konstantami | https://github.com/tisnik/c3-examples/blob/master/c3-macros/07_compile_time1.c3 |
| 156 | 08_compile_time2.c3 | expanze makra v čase překladu: funkční varianta s konstantami | https://github.com/tisnik/c3-examples/blob/master/c3-macros/08_compile_time2.c3 |
| 157 | 09_compile_time3.c3 | expanze makra v čase překladu: nefunkční varianta s proměnnými | https://github.com/tisnik/c3-examples/blob/master/c3-macros/09_compile_time3.c3 |
| 158 | 10_swap_macro1.c3 | realizace makra pro prohození obsahu dvou proměnných: nefunkční varianta | https://github.com/tisnik/c3-examples/blob/master/c3-macros/10_swap_macro1.c3 |
| 159 | 11_swap_macro2.c3 | realizace makra pro prohození obsahu dvou proměnných: funkční varianta | https://github.com/tisnik/c3-examples/blob/master/c3-macros/11_swap_macro2.c3 |
| 160 | 12_varargs.c3 | makro s proměnným počtem argumentů | https://github.com/tisnik/c3-examples/blob/master/c3-macros/12_varargs.c3 |
Demonstrační příklady uvedené v dnešním článku:
20. Odkazy na Internetu
- Programovací jazyk C3: evoluce, nikoli revoluce
https://www.root.cz/clanky/programovaci-jazyk-c3-evoluce-nikoli-revoluce/ - Programovací jazyk C3: datové typy pro moderní architektury
https://www.root.cz/clanky/programovaci-jazyk-c3-datove-typy-pro-moderni-architektury/ - Programovací jazyk C3: složené datové typy a kontejnery
https://www.root.cz/clanky/programovaci-jazyk-c3-slozene-datove-typy-a-kontejnery/ - The C3 Programming Language
https://c3-lang.org/ - C3 For C Programmers
https://c3-lang.org/language-overview/primer/ - C3 is a C-like language trying to be an incremental improvement over C rather than a whole new language
https://www.reddit.com/r/ProgrammingLanguages/comments/oohij6/c3_is_a_clike_language_trying_to_be_an/ - Tiobe index
https://www.tiobe.com/tiobe-index/ - PYPL PopularitY of Programming Language
https://pypl.github.io/PYPL.html - C3 Tutorial
https://learn-c3.org/ - History of programming languages
https://devskiller.com/history-of-programming-languages/ - History of programming languages (Wikipedia)
https://en.wikipedia.org/wiki/History_of_programming_languages - D language
https://dlang.org/ - Zig programming language
https://ziglang.org/ - V language
https://vlang.io/ - D programming language
https://en.wikipedia.org/wiki/D_(programming_language) - Zig programming language (Wikipedia)
https://en.wikipedia.org/wiki/Zig_(programming_language) - V programming language (Wikipedia)
https://en.wikipedia.org/wiki/V_(programming_language) - Syntax highlighting for C3's programming language
https://github.com/Airbus5717/c3.vim - Go factorial
https://gist.github.com/esimov/9622710 - Generational list of programming languages
https://en.wikipedia.org/wiki/Generational_list_of_programming_languages - The Language Tree: Almost Every Programming Language Ever Made
https://github.com/Phileosopher/langmap - List of C-family programming languages
https://en.wikipedia.org/wiki/List_of_C-family_programming_languages - Compatibility of C and C++
https://en.wikipedia.org/wiki/Compatibility_of_C_and_C%2B%2B - C++23: compatibility with C
https://www.sandordargo.com/blog/2023/08/23/cpp23-c-compatibility - Can C++ Run C Code? Understanding Language Compatibility
https://www.codewithc.com/can-c-run-c-code-understanding-language-compatibility/ - C3: Comparisons With Other Languages
https://c3-lang.org/faq/compare-languages/ - C3 Programming Language Gains Traction as Modern C Alternative
https://biggo.com/news/202504040125_C3_Programming_Language_Alternative_to_C - The case against a C alternative
https://c3.handmade.network/blog/p/8486-the_case_against_a_c_alternative - C (programming language) Alternatives
https://alternativeto.net/software/c-programming-language-/ - Seriál Programovací jazyk Go
https://www.root.cz/serialy/programovaci-jazyk-go/ - Is C3 the Underdog That Will Overtake Zig and Odin?
https://bitshifters.cc/2025/05/22/c3-c-tradition.html - „Hello, World!“ program
https://en.wikipedia.org/wiki/%22Hello%2C_World!%22_program - The C Programming Language
https://en.wikipedia.org/wiki/The_C_Programming_Language - Kontejner (abstraktní datový typ)
https://cs.wikipedia.org/wiki/Kontejner_(abstraktn%C3%AD_datov%C3%BD_typ) - Are arrays not considered containers because they are not based off of a class?
https://stackoverflow.com/questions/37710975/are-arrays-not-considered-containers-because-they-are-not-based-off-of-a-class - Array declaration (C, C++)
https://en.cppreference.com/w/cpp/language/array.html - Understanding the Apple ‘goto fail;’ vulnerability
https://www.blackduck.com/blog/understanding-apple-goto-fail-vulnerability-2.html - Branch (computer science)
https://en.wikipedia.org/wiki/Branch_(computer_science) - Conditional (computer programming)
https://en.wikipedia.org/wiki/Conditional_(computer_programming) - Dangling else
https://en.wikipedia.org/wiki/Dangling_else - Switch statement
https://en.wikipedia.org/wiki/Switch_statement - Compiler correctness
https://en.wikipedia.org/wiki/Compiler_correctness - Anonymous function
https://en.wikipedia.org/wiki/Anonymous_function - Closure (computer programming)
https://en.wikipedia.org/wiki/Closure_(computer_programming) - How to implement closures in C
https://hokstad.com/how-to-implement-closures - An Overview of Macros in Rust
http://words.steveklabnik.com/an-overview-of-macros-in-rust - A Practical Intro to Macros in Rust 1.0
https://danielkeep.github.io/practical-intro-to-macros.html - The Rust Programming Language: macros
https://doc.rust-lang.org/beta/book/macros.html - Rust by example: 15 macro_rules!
http://rustbyexample.com/macros.html - Undefined behavior
https://en.wikipedia.org/wiki/Undefined_behavior - Undefined Behavior in C and C++
https://www.geeksforgeeks.org/cpp/undefined-behavior-c-cpp/ - The Rust reference: Behavior considered undefined
https://doc.rust-lang.org/reference/behavior-considered-undefined.html#behavior-considered-undefined - Why would a language have a concept of undefined behavior instead of raising an error?
https://langdev.stackexchange.com/questions/481/why-would-a-language-have-a-concept-of-undefined-behavior-instead-of-raising-an - Undefined behavior
https://riptutorial.com/c/topic/364/undefined-behavior - C3: Undefined Behaviour
https://c3-lang.org/language-rules/undefined-behaviour/ - Undefined behavior in C and C++
https://russellw.github.io/undefined-behavior
