Obsah
1. Funkce v programovacím jazyku C3 (dokončení)
3. Komplikovanější ukázky zkráceného zápisu těl funkcí
6. Volání funkce reduce s předáním anonymní funkce v prvním parametru
7. Přímé zavolání právě definované anonymní funkce
10. Funkce vracející jinou funkci
11. Úplný zdrojový kód demonstračního příkladu: funkce vyššího řádu v jazyku C3
12. Volání nativních céčkovských funkcí
13. Volání nativní céčkové funkce ze staticky či dynamicky linkované knihovny
14. Datové typy používané při volání nativních céčkových funkcí
15. Demonstrační příklad – použití korektních datových typů pro volání nativní funkce
16. Volání nativní funkce, která akceptuje ukazatel na (první) prvek pole
19. Repositář s demonstračními příklady
1. Funkce v programovacím jazyku C3 (dokončení)
V předchozím článku o programovacím jazyku C3 jsme si ukázali, jakým způsobem se pracuje s funkcemi, které tvoří základní (a velmi pravděpodobně i nejdůležitější) úroveň abstrakce. Připomeňme si, že kromě běžných funkcí s pozičními argumenty podporuje jazyk C3 i specifikaci výchozích hodnot parametrů funkce, při volání funkce je možné explicitně uvést jména parametrů a podporovány jsou i funkce s proměnným počtem parametrů (s nimiž se pracuje jednodušeji, než s podobnými funkcemi v jazyku C). Ovšem v C3 lze pracovat i s hodnotami typu Optional a proto existuje speciální operátor používaný při volání funkcí vracejících tyto hodnoty. A nakonec jsme si popsali takzvané kontrakty a specifikaci, zda jsou hodnoty předávané ukazatelem určeny pouze pro čtení nebo i pro zápis.
Dnes popis použití funkcí v programovacím jazyku C3 dokončíme. Ukážeme si způsob zkráceného zápisu těla funkcí, definici a volání (či předání) anonymních funkcí, přesvědčíme se, že jazyk C3 nepodporuje uzávěry, popíšeme si způsob volání nativních céčkovských funkcí a nakonec si ukážeme volání takzvaných metod, což jsou funkce navázané na konkrétní datový typ.
2. Zkrácený zápis těl funkcí
Programovací jazyk C3 podporuje takzvaný zkrácený zápis funkcí, který se využije tehdy, pokud je možné funkci přepsat do podoby, kdy její tělo obsahuje pouze příkaz return (s libovolně složitým výrazem). Takové funkce se v praxi používají poměrně často a jejich „plný“ zápis může být zbytečně dlouhý. Příkladem může být funkce nazvaná add, v jejímž těle se skutečně nachází pouze jediný příkaz return:
module functions;
import std::io;
fn int add(int a, int b)
{
return a+b;
}
fn void main()
{
io::printf("%d+%d=%d\n", 1, 2, add(1, 2));
}
Funkci add můžeme zkrátit takovým způsobem, že její hlavička i tělo (tvořené jediným příkazem return) budou zapsány na jediném programovém řádku. Namísto běžného těla s příkazem return se zapíše šipka =>, za kterou následuje výraz. Po zavolání funkce bude tento výraz vyhodnocen a jeho výsledek bude současně i návratovou hodnotou volané funkce:
module functions;
import std::io;
fn int add(int a, int b) => a+b;
fn void main()
{
io::printf("%d+%d=%d\n", 1, 2, add(1, 2));
}
3. Komplikovanější ukázky zkráceného zápisu těl funkcí
Podívejme se na další funkce, které jsou zapsány zkráceným způsobem. Vždy si nejdříve ukážeme původní zdrojový kód příkladu s běžně zapsanou funkcí a posléze zkrácený zápis funkce.
Funkce add s výchozí hodnotou svého druhého parametru:
module functions;
import std::io;
fn int add(int a, int b=0)
{
return a+b;
}
fn void main()
{
io::printf("%d+%d=%d\n", 1, 0, add(1));
io::printf("%d+%d=%d\n", 1, 2, add(1, 2));
}
Zkrácený způsob zápisu funkce add:
module functions;
import std::io;
fn int add(int a, int b=0) => a+b;
fn void main()
{
io::printf("%d+%d=%d\n", 1, 0, add(1));
io::printf("%d+%d=%d\n", 1, 2, add(1, 2));
}
Funkce add, která má specifikovány výchozí hodnoty pro oba své parametry:
module functions;
import std::io;
fn int add(int a=0, int b=0)
{
return a+b;
}
fn void main()
{
io::printf("%d+%d=%d\n", 0, 0, add());
io::printf("%d+%d=%d\n", 1, 0, add(1));
io::printf("%d+%d=%d\n", 1, 2, add(1, 2));
}
Zkrácený způsob zápisu funkce add:
module functions;
import std::io;
fn int add(int a=0, int b=0) => a+b;
fn void main()
{
io::printf("%d+%d=%d\n", 0, 0, add());
io::printf("%d+%d=%d\n", 1, 0, add(1));
io::printf("%d+%d=%d\n", 1, 2, add(1, 2));
}
Funkce div s běžnou dvojicí parametrů. Při volání funkce jsou explicitně uvedena jména parametrů:
module functions;
import std::io;
fn int div(int a, int b)
{
return a/b;
}
fn void main()
{
io::printf("%d/%d=%d\n", 10, 2, div(a:10, b:2));
io::printf("%d/%d=%d\n", 10, 3, div(a:10, b:3));
}
Zkrácený způsob zápisu funkce div:
module functions;
import std::io;
fn int div(int a, int b) => a/b;
fn void main()
{
io::printf("%d/%d=%d\n", 10, 2, div(a:10, b:2));
io::printf("%d/%d=%d\n", 10, 3, div(a:10, b:3));
}
Funkce div s výchozí hodnotou parametrů a navíc vracející hodnotu typu Optional:
module functions;
import std::io;
faultdef ZERO_INPUT;
fn int? div(int a=1, int b=1)
{
if (b==0) {
return ZERO_INPUT?;
}
return a/b;
}
fn void main()
{
(void)io::printf("%d/%d=%d\n", 1, 1, div());
(void)io::printf("%d/%d=%d\n", 10, 1, div(a:10));
(void)io::printf("%d/%d=%d\n", 10, 2, div(a:10, b:2));
(void)io::printf("%d/%d=%d\n", 10, 3, div(a:10, b:3));
(void)io::printf("%d/%d=%d\n", 10, 0, div(a:10, b:0));
}
Ve zkráceném zápisu takto upravené funkce div je nutné použít ternární operátor namísto programové konstrukce if-then-else:
module functions;
import std::io;
faultdef ZERO_INPUT;
fn int? div(int a=1, int b=1) => b==0 ? ZERO_INPUT? : a/b;
fn void main()
{
(void)io::printf("%d/%d=%d\n", 1, 1, div());
(void)io::printf("%d/%d=%d\n", 10, 1, div(a:10));
(void)io::printf("%d/%d=%d\n", 10, 2, div(a:10, b:2));
(void)io::printf("%d/%d=%d\n", 10, 3, div(a:10, b:3));
(void)io::printf("%d/%d=%d\n", 10, 0, div(a:10, b:0));
}
Funkce first akceptující proměnný počet parametrů:
module functions;
import std::io;
fn int first(int... args)
{
int f = args[0];
return f;
}
fn void main()
{
int[10] values = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
io::printf("%d\n", first(1));
io::printf("%d\n", first(1, 2));
io::printf("%d\n", first(1, 2, 3));
io::printf("%d\n", first(...values));
}
Zkrácený způsob zápisu funkce first:
module functions;
import std::io;
fn int first(int... args) => args[0];
fn void main()
{
int[10] values = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
io::printf("%d\n", first(1));
io::printf("%d\n", first(1, 2));
io::printf("%d\n", first(1, 2, 3));
io::printf("%d\n", first(...values));
}
4. Anonymní funkce
Programovací jazyk C3 podporuje i zápis anonymních funkcí (anonymous functions), které se od „běžných“ funkcí liší tím, že v jejich definici není použito jméno definované funkce. Definice anonymní funkce může být uvedena v takových místech zdrojového kódu, ve kterém se očekává zápis parametru volané funkce, definice lokální hodnoty atd.
Nejdříve se podívejme na demonstrační příklad, ve kterém anonymní funkce nejsou použity. V příkladu je definována dvojice běžných (neanonymních) funkcí, které provedou součet (sumu) či součin (produkt) všech svých parametrů a vrátí výsledek tohoto výpočtu. Tyto funkce jsou volány z main:
module functions;
import std::io;
fn int reduce_add(int... args)
{
int acc = 0;
foreach (arg : args) {
acc += arg;
}
return acc;
}
fn int reduce_mul(int... args)
{
int acc = 1;
foreach (arg : args) {
acc *= arg;
}
return acc;
}
fn void main()
{
int[10] values = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
io::printf("%d\n", reduce_add(...values));
io::printf("%d\n", reduce_mul(...values));
}
Tento příklad je sice plně funkční, ovšem opakuje se v něm prakticky stejná část kódu – konkrétně programová smyčka foreach. Bylo by tedy možná vhodné nahradit obě dvě logicky stejné funkce nějakou obecnější funkcí. A právě zde je možné použít anonymní funkce.
5. Funkce typu reduce
Demonstrační příklad z předchozí kapitoly je možné přepsat do obecnější podoby. Nejprve v něm definujeme nový datový typ „funkce“, konkrétně „funkce akceptující dva parametry typu int a vracející hodnotu typu int“:
alias ReduceOpp = fn int(int, int);
Následně můžeme napsat dosti obecnou funkci nazvanou reduce, která akceptuje tři parametry:
- Funkci typu ReduceOpp, která postupně zpracuje n vstupních hodnot (ano, funkci, resp. její adresu je možné předat do jiné funkce)
- Výchozí hodnotu uloženou do akumulátoru ještě před postupným zpracováním (redukcí) předaných hodnot
- Proměnný počet parametrů, které budou postupně zpracovány (redukovány) funkcí předanou v prvním parametru
Funkce reduce může vypadat následovně:
fn int reduce(ReduceOpp operation, int init, int... args)
{
int acc = init;
foreach (arg : args) {
acc = operation(acc, arg);
}
return acc;
}
6. Volání funkce reduce s předáním anonymní funkce v prvním parametru
Výše uvedenou funkci reduce můžeme použít jako pro výpočet sumy, tak i pro vynásobení všech předaných hodnot. Pro výpočet sumy postačuje zavolat:
int vysledek = reduce(fn (int a, b) => a+b, 0, ...values);
Naopak vynásobení všech předaných hodnot lze realizovat takto:
int vysledek = reduce(fn (int a, b) => a*b, 1, ...values);
Příklad použití:
module functions;
import std::io;
alias ReduceOpp = fn int(int, int);
fn int reduce(ReduceOpp operation, int init, int... args)
{
int acc = init;
foreach (arg : args) {
acc = operation(acc, arg);
}
return acc;
}
fn void main()
{
int[10] values = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
io::printf("%d\n", reduce(fn (int a, b) => a+b, 0, ...values));
io::printf("%d\n", reduce(fn (int a, b) => a*b, 1, ...values));
}
Ověřme si funkčnost tohoto programu:
$ c3c compile-run 33_anonymous.c3 Program linked to executable './functions'. Launching ./functions 55 3628800
7. Přímé zavolání právě definované anonymní funkce
Zápis anonymní funkce pro překladač jazyka C3 znamená, že taková funkce bude přeložena do strojového kódu a bude ji tak možné ihned zavolat. To lze pochopitelně provést i v praxi. V následujícím demonstračním příkladu je (kromě dalších definic) definována i anonymní funkce, která vynásobí hodnotu předaného celočíselného parametru dvěma a vrátí výsledek tohoto součinu. Tato funkce je ihned (na stejném programovém řádku) zavolána – je jí předána hodnota proměnné x a výsledek je uložen do proměnné y. Povšimněte si zejména toho, jak je definice anonymní funkce umístěna do kulatých závorek:
module functions;
import std::io;
fn void main()
{
int x = 10;
int y = (fn int(int a) => a*2)(x);
io::printf("%d\n", x);
io::printf("%d\n", y);
}
Ověření funkcionality tohoto řešení:
$ c3c compile-run 34_anonymous.c3 Program linked to executable './functions'. Launching ./functions 10 20
8. Uzávěry v jazyce C3?
Vzhledem k tomu, že překladač programovacího jazyka C3 akceptuje zkrácený zápis funkcí a současně i podporuje práci s anonymními funkcemi, by se mohlo zdát, že jsou podporovány i uzávěry (closures), tj. funkce, které mají přístup ke svému okolí (prostředí, environment). Ovšem ve skutečnosti tomu tak není, což si můžeme velmi snadno ověřit na následujícím příkladu, ve kterém se snažíme v těle anonymní funkce přistoupit (dokonce jen přečíst, nikoli modifikovat) k lokální proměnné b:
module functions;
import std::io;
fn void main()
{
int x = 10;
int b = 2;
int y = (fn int(int a) => a*b)(x);
io::printf("%d\n", x);
io::printf("%d\n", y);
}
Při pokusu o překlad tohoto příkladu dojde k chybě při překladu, protože z těla anonymní funkce není proměnná b viditelná:
$ c3c compile-run 35_anonymous.c3
6: int x = 10;
7: int b = 2;
8:
9: int y = (fn int(int a) => a*b)(x);
^
(/home/ptisnovs/src/c3-examples/c3-functions/35_anonymous.c3:9:33) Error: 'b' could not be found, did you spell it right?
9. Ukazatele na funkce
V předchozích kapitolách jsme si ukázali zdrojový kód, ve kterém se do funkce reduce předávaly anonymní funkce:
io::printf("%d\n", reduce(fn (int a, b) => a+b, 0, ...values));
io::printf("%d\n", reduce(fn (int a, b) => a*b, 1, ...values));
Jak se ovšem takový příklad musí upravit ve chvíli, kdy budeme chtít předat běžné (pojmenované) funkce, například funkce add a mul?
fn int add(int a, int b)
{
return a+b;
}
fn int mul(int a, int b)
{
return a*b;
}
V tomto případě je nutné předat nikoli jméno funkce, ale ukazatel na funkci, tj. zápis musí vypadat takto:
io::printf("%d\n", reduce(&add, 0, ...values));
io::printf("%d\n", reduce(&mul, 1, ...values));
Chování jazyka C3 si otestujeme na takto upraveném demonstračním příkladu:
module functions;
import std::io;
alias ReduceOpp = fn int(int, int);
fn int add(int a, int b)
{
return a+b;
}
fn int mul(int a, int b)
{
return a*b;
}
fn int reduce(ReduceOpp operation, int init, int... args)
{
int acc = init;
foreach (arg : args) {
acc = operation(acc, arg);
}
return acc;
}
fn void main()
{
int[10] values = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
io::printf("%d\n", reduce(&add, 0, ...values));
io::printf("%d\n", reduce(&mul, 1, ...values));
}
Samozřejmě je i v tomto případě stále možné použít alternativní způsob definice funkcí add a mul s tělem obsahujícím jen příkaz return zapsaný krátkým způsobem:
module functions;
import std::io;
alias ReduceOpp = fn int(int, int);
fn int add(int a, int b) => a+b;
fn int mul(int a, int b) => a*b;
fn int reduce(ReduceOpp operation, int init, int... args)
{
int acc = init;
foreach (arg : args) {
acc = operation(acc, arg);
}
return acc;
}
fn void main()
{
int[10] values = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
io::printf("%d\n", reduce(&add, 0, ...values));
io::printf("%d\n", reduce(&mul, 1, ...values));
}
10. Funkce vracející jinou funkci
V rámci předchozích kapitol jsme si ukázali, jakým způsobem je možné předat do nějaké funkce jinou funkci (resp. ukazatel na ni). Podobně lze funkci z jiné funkce vrátit – funkce (interně ukazatel) jsou totiž v programovacím jazyku C3 považovány za plnohodnotný datový typ. Ukažme si to na příkladu funkce, která vrátí implementaci nějakého binárního aritmetického operátoru. Typ návratového hodnoty je definován následovně:
alias ReduceOpp = fn int(int, int);
Funkce nazvaná get_operation_implementation vrátí na základě předaného parametru (jeden znak s reprezentací binárního aritmetického operátoru) implementaci takového operátoru. Vracet budeme anonymní funkce:
fn ReduceOpp get_operation_implementation(char operator)
{
switch (operator) {
case '+':
return fn (int a, b) => a+b;
case '-':
return fn (int a, b) => a-b;
case '*':
return fn (int a, b) => a*b;
case '/':
return fn (int a, b) => a/b;
default:
return fn (int a, b) => 0;
}
}
Do funkce reduce, kterou již známe, budeme předávat právě hodnotu získanou voláním funkce get_operation_implementation:
int[10] values = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
io::printf("%d\n", reduce(get_operation_implementation('+'), 0, ...values));
io::printf("%d\n", reduce(get_operation_implementation('-'), 0, ...values));
io::printf("%d\n", reduce(get_operation_implementation('*'), 1, ...values));
io::printf("%d\n", reduce(get_operation_implementation('/'), 1000000000, ...values));
S výsledky:
55 -55 3628800 275
11. Úplný zdrojový kód demonstračního příkladu: funkce vyššího řádu v jazyku C3
Ukažme si pro úplnost celý zdrojový kód takto naprogramovaného demonstračního příkladu:
module functions;
import std::io;
alias ReduceOpp = fn int(int, int);
fn ReduceOpp get_operation_implementation(char operator)
{
switch (operator) {
case '+':
return fn (int a, b) => a+b;
case '-':
return fn (int a, b) => a-b;
case '*':
return fn (int a, b) => a*b;
case '/':
return fn (int a, b) => a/b;
default:
return fn (int a, b) => 0;
}
}
fn int reduce(ReduceOpp operation, int init, int... args)
{
int acc = init;
foreach (arg : args) {
acc = operation(acc, arg);
}
return acc;
}
fn void main()
{
int[10] values = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
io::printf("%d\n", reduce(get_operation_implementation('+'), 0, ...values));
io::printf("%d\n", reduce(get_operation_implementation('-'), 0, ...values));
io::printf("%d\n", reduce(get_operation_implementation('*'), 1, ...values));
io::printf("%d\n", reduce(get_operation_implementation('/'), 1000000000, ...values));
}
12. Volání nativních céčkovských funkcí
Jednou z velkých předností programovacího jazyka C3 je jeho téměř bezproblémové rozhraní umožňující volání nativních funkcí, které jsou typicky naprogramovány v jazyku C. Ve zdrojovém kódu C3 je nutné uvést hlavičku volané nativní funkce. Následně je možné takovou funkci přímo volat a předávat jí nativní hodnoty spravované jazykem C3. Vzhledem k tomu, že oba programovací jazyky jsou od sebe v některých ohledech odlišné, není rozhraní C3 ↔ C zcela bezproblémové. Na některé nejdůležitější rozdíly se podíváme v dalších kapitolách.
Nejjednodušší je situace ve chvíli, kdy potřebujeme volat nějakou funkci ze standardní knihovny jazyka C. V takovém případě nemusíme upravovat způsob překladu programu; pouze uvedeme hlavičku volané funkce (zde puts) a následně ji můžeme volat naprosto stejným způsobem, jako jakoukoli jinou funkci definovanou přímo v jazyku C3:
module functions;
extern fn void puts(char*);
fn void main()
{
puts("Hello, world!");
}
13. Volání nativní céčkové funkce ze staticky či dynamicky linkované knihovny
Vyzkoušejme si nyní zavolat nativní céčkovou funkci, která bude přeložena do staticky nebo dynamicky linkované knihovny. Definice takové funkce (v céčku) je triviální:
extern int add(int x, int y)
{
return x+y;
}
Provedeme překlad této funkce do objektového kódu s vložením tohoto kódu do sdílené knihovny libadder.so:
gcc -Wall -ansi -c -fPIC adder.c -o adder.o gcc -shared -Wl,-soname,libadder.so -o libadder.so adder.o
Volání takové funkce z jazyka C3 je snadné:
module functions;
import std::io;
extern fn int add(int a, int b);
fn void main()
{
io::printf("%d\n", add(1, 2));
}
Při překladu tohoto příkladu je nutné překladači předat i jméno sdílené knihovny:
$ c3c compile -l libadder.so 40_call_c_function.c3 Program linked to executable './functions'.
Před spuštěním takto přeloženého programu si nastavte cestu ke sdíleným knihovnám:
$ export LD_LIBRARY_PATH=.
Ověření funkcionality:
$ ./functions 3
14. Datové typy používané při volání nativních céčkových funkcí
Při volání nativních céčkových funkcí je nutné si dát pozor na správné použití datových typů. Jazyk C3 programátorům nabízí běžnou „sadu“ celočíselných datových typů, a to jak typů se znaménkem, tak i bez znaménka. U všech těchto typů je definitoricky určena jejich velikost v bajtech i rozsah hodnot (minimální a maximální hodnota) – a to na všech platformách. V céčku je tomu však odlišně a proto nebývá vhodné používat v předběžné deklaraci externích (nativních) funkcí základní typy jazyka C3, ale typy, jejichž název začíná velkým C:
| Typ v C3 | Odpovídající typ v C |
|---|---|
| CChar | char |
| CShort | short int |
| CUShort | unsigned short int |
| CInt | int |
| CUInt | unsigned int |
| CLong | long int |
| CULong | unsigned long int |
| CLongLong | long long |
| CULongLong | unsigned long long |
| CLongDouble | long double |
15. Demonstrační příklad – použití korektních datových typů pro volání nativní funkce
Podívejme se nyní na způsob použití datových typů uvedených v předchozí kapitole. Mějme céčkovskou funkci, která dokáže vynásobit dvojici hodnot typu long, přičemž výsledek bude taktéž typu long:
extern long multiply(long x, long y)
{
return x*y;
}
Ovšem typ long v jazyku C nemusí být totožný s typem long v jazyku C3. Rozdíly budou viditelné například na 32bitových platformách. Proto musíme na straně C3 použít typ CLong a ponechat na překladači C3 typovou analýzu a popř. hlášení o nekompatibilních datových typech:
extern fn CLong multiply(CLong a, CLong b);
Celý zdrojový kód demonstračního příkladu, který bude volat nativní céčkovskou funkci multiply, může vypadat následovně:
module functions;
import std::io;
extern fn CLong multiply(CLong a, CLong b);
fn void main()
{
long a = 6L;
long b = 7L;
io::printf("%d\n", multiply(a, b));
io::printf("%d\n", a*b);
io::printf("%b\n", a*b == multiply(a, b));
}
16. Volání nativní funkce, která akceptuje ukazatel na (první) prvek pole
V praxi se velmi často setkáme s nativními funkcemi, které jako svůj parametr akceptují pole. Příkladem může být céčkovská funkce, která sečte všech length prvků pole:
extern int sum(int values[], int length)
{
int i;
int acc = 0;
for (i=0; i<length; i++) {
acc += values[i];
}
return acc;
}
Alternativní způsob zápisu takové funkce naznačuje, že se pole ve skutečnosti předává odkazem, konkrétně jako ukazatel na první prvek pole:
extern int sum(int *values, int length)
{
int i;
int acc = 0;
for (i=0; i<length; i++) {
acc += values[i];
}
return acc;
}
Na straně jazyka C3 je nutné specifikovat, že se skutečně bude předávat ukazatel. To je nutné, protože zápis s prázdnými hranatými závorkami bude mít odlišný význam:
extern fn int sum(int *values, int length);
Příklad volání funkce sum z jazyka C3:
int[10] values = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
io::printf("%d\n", sum(&values, 10));
Celý demonstrační příklad by mohl vypadat následovně:
module functions;
import std::io;
extern fn int sum(int *values, int length);
fn void main()
{
int[10] values = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
io::printf("%d\n", sum(&values, 10));
}
Výsledek:
55
Ovšem můžeme taktéž využít faktu, že jazyk C3 podporuje řezy a psát:
int[10] values = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int[] slice = &values;
io::printf("%d\n", sum(slice, slice.len));
Upravený demonstrační příklad nyní bude vypadat následovně:
module functions;
import std::io;
extern fn int sum(int *values, int length);
fn void main()
{
int[10] values = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int[] slice = &values;
io::printf("%d\n", sum(slice, slice.len));
}
17. Metody v jazyce C3
V programovacím jazyce C3 lze definovat i metody. Jedná se vlastně o funkce vztažené k nějakému datovému typu. Metody je možné volat dvěma rozličnými způsoby, které si oba ukážeme.
Nejprve si zadefinujeme nějaký uživatelský datový typ. Pro jednoduchost se může jednat o reprezentaci vektoru v 3D prostoru pomocí tří složek:
struct Vector3d
{
int x;
int y;
int z;
}
Pokud budeme chtít pro tento datový typ definovat metodu, například metodu pro součet dvou vektorů, může řešení vypadat následovně. Tato metoda mění (mutuje) první vektor, který je jí předán:
fn void Vector3d.add(Vector3d* first, Vector3d* second)
{
first.x += second.x;
first.y += second.y;
first.z += second.z;
}
Výše uvedenou metodu můžeme volat tak, jakoby se jednalo o funkci umístěnou ve jmenném prostoru Vector3d:
Vector3d v1 = {1, 2, 3};
Vector3d v2 = {10, 10, 10};
Vector3d.add(&v1, &v2);
Celý příklad s definicí metody i s jejím voláním může vypadat následovně:
module functions;
import std::io;
struct Vector3d
{
int x;
int y;
int z;
}
fn void Vector3d.add(Vector3d* first, Vector3d* second)
{
first.x += second.x;
first.y += second.y;
first.z += second.z;
}
fn void main()
{
Vector3d v1 = {1, 2, 3};
Vector3d v2 = {10, 10, 10};
Vector3d.add(&v1, &v2);
io::printf("[%d %d %d]\n", v1.x, v1.y, v1.z);
}
Otestování funkcionality tohoto demonstračního příkladu:
[11 12 13]
Varianta, ve které je druhý vektor předán hodnotou:
module functions;
import std::io;
struct Vector3d
{
int x;
int y;
int z;
}
fn void Vector3d.add(Vector3d* first, Vector3d second)
{
first.x += second.x;
first.y += second.y;
first.z += second.z;
}
fn void main()
{
Vector3d v1 = {1, 2, 3};
Vector3d v2 = {10, 10, 10};
Vector3d.add(&v1, v2);
io::printf("[%d %d %d]\n", v1.x, v1.y, v1.z);
}
Ovšem na druhou stranu další příklad již funkční nebude, resp. nebude modifikovat první vektor předaný do metody!
module functions;
import std::io;
struct Vector3d
{
int x;
int y;
int z;
}
fn void Vector3d.add(Vector3d first, Vector3d second)
{
first.x += second.x;
first.y += second.y;
first.z += second.z;
}
fn void main()
{
Vector3d v1 = {1, 2, 3};
Vector3d v2 = {10, 10, 10};
Vector3d.add(v1, v2);
io::printf("[%d %d %d]\n", v1.x, v1.y, v1.z);
}
18. Druhý způsob volání metod
Ve skutečnosti není výše uvedené volání metody:
Vector3d.add(&v1, &v2);
v mnoha programovacích jazycích příliš idiomatické. Z tohoto důvodu nabízí programovací jazyk C3 i známější a dnes používanější způsob zápisu volání metody:
v1.add(&v2);
Pro úplnost si uveďme úplný zdrojový kód demonstračního příkladu upraveného do podoby, ve které se používá idiomatičtější způsob zápisu volání metody:
module functions;
import std::io;
struct Vector3d
{
int x;
int y;
int z;
}
fn void Vector3d.add(Vector3d* first, Vector3d* second)
{
first.x += second.x;
first.y += second.y;
first.z += second.z;
}
fn void main()
{
Vector3d v1 = {1, 2, 3};
Vector3d v2 = {10, 10, 10};
v1.add(&v2);
io::printf("[%d %d %d]\n", v1.x, v1.y, v1.z);
}
Otestování funkcionality:
[11 12 13]
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 |
A konečně následují odkazy na demonstrační příklady z článku dnešního:
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