Obsah
1. Programovací jazyk C3: evoluce, nikoli revoluce
2. Instalace balíčků vyžadovaných pro překlad C3
5. Je jazyk C3 zpětně kompatibilní s jazykem C?
6. Porovnání syntaxe jazyka C s dalšími mainstreamovými programovacími jazyky
7. První reálný program vytvořený v jazyku C3
8. Makra v programovacím jazyku C3
10. Definice datové struktury s rozměry rastrového obrázku
13. Statická kontrola, zda se nepředávají neinicializované ukazatele
14. Kontrola neinicializovaných ukazatelů v čase běhu programu
15. Přepis funkce pro inicializaci palety a striktnější typová kontrola
16. Výpočty barev v barvové paletě s hodnotami typu char a nikoli int
17. Volání knihovní I/O funkce a volání nativní céčkovské I/O funkce
18. Funkční varianta céčkovského programu z deváté kapitoly
19. Repositář s demonstračními příklady
1. Programovací jazyk C3: evoluce, nikoli revoluce
V perexu dnešního článku jsme si připomenuli, že jeden z nejpoužívanějších programovacích jazyků vůbec – C – vznikl před více než padesáti lety. A i když se C postupně vyvíjelo, nikdy nedošlo k nějaké dramatické a zpětně nekompatibilní změně. Jazyk C je tak (i přesto, že existuje několik oficiálních standardů) velmi konzervativní a stabilní. Na druhou stranu se vyvíjí samotná oblast IT, takže není divu, že vzniklo relativně velké množství jazyků, které jsou od jazyka C více či méně odvozeny (někdy zcela – viz první verze C++, jindy je zde patrná velká inspirace, jako je tomu u jazyků V, Zig a vlastně i u Go a Rustu). Relativně novým členem v rodině jazyků odvozených od céčka je jazyk nazvaný C3.
C3 se – i přes zdánlivou značnou podobnost – ve skutečnosti od klasického céčka v mnoha ohledech odlišuje. Většina nově přidaných vlastností zjednodušuje tvorbu programů, umožňuje psát bezpečnější programy (resp. zjednodušuje tvorbu bezpečnějších programů, protože C toto nechává zcela na programátorovi) a přidává do jazyka další užitečné vlastnosti. Jednotlivými vlastnostmi programovacího jazyka C3 se budeme podrobněji zabývat v samostatných článcích, takže si prozatím tyto vlastnosti alespoň ve stručnosti vypišme:
- Je zaručena kompatibilita s céčkovým ABI (velmi důležité)
- Namísto vkládání hlavičkových souborů se používá systém modulů
- Moduly mají vlastní jmenné prostory, takže nedochází ke konfliktům u stejně pojmenovaných identifikátorů
- Lze zapisovat podmínky, které mají být splněny před a po příkazu/funkci
- Lze definovat vlastnosti ukazatelů předávaných do funkcí (zda mohou být null atd.)
- Plná podpora pro UTF-8
- Možnost práce s řezy (podobně jako v Go, opět velmi užitečné)
- Nový systém maker; jde o skutečná makra, nikoli o textové substituce
- V jazyku je méně explicitně nedefinovaných chování (tím je C proslulé, a je k tomu dobrý důvod)
Jazyk C3 je na poli programovacích jazyků skutečně nováčkem. Ostatně prozatím ani nebyl přidán do seznamu jazyků více či méně odvozených od jazyka C (kam bezpochyby patří). Tento seznam lze nalézt na adrese https://en.wikipedia.org/wiki/List_of_C-family_programming_languages (najdeme zde „staré známé“, včetně C++, Rustu i Go).
2. Instalace balíčků vyžadovaných pro překlad C3
V navazující kapitole si popíšeme způsob překladu všech základních nástrojů programovacího jazyka C3, protože tyto nástroje ještě nebývají součástí distribucí Linuxu (popř. jsou zastaralé) a není je tedy možné nainstalovat přímo. Ovšem pro samotný překlad a slinkování C3 je nutné, aby byly v systému nainstalovány další překladače (Clang, Git, cmake) a knihovny (LLVM-devel atd.). Ukažme si instalaci balíčků s těmito nástroji v systémech s RPM/DNF:
$ sudo dnf install cmake clang git llvm llvm-devel lld lld-devel ncurses-devel
Samotný průběh instalace pochopitelně do značné míry závisí na tom, které balíčky již byly nainstalovány (zde například cmake a git) a které nikoli:
Last metadata expiration check: 0:14:31 ago on Fri 29 Aug 2025 11:36:33 AM CEST. Package cmake-3.30.8-1.fc41.x86_64 is already installed. Package git-2.49.0-1.fc41.x86_64 is already installed. Dependencies resolved. ======================================================================================================================================== Package Architecture Version Repository Size ======================================================================================================================================== Installing: clang x86_64 18.1.8-2.fc41 updates 78 k lld x86_64 18.1.8-1.fc41 updates 36 k lld-devel x86_64 18.1.8-1.fc41 updates 25 k llvm x86_64 18.1.8-4.fc41 updates 27 M llvm-devel x86_64 18.1.8-4.fc41 updates 4.1 M ncurses-devel x86_64 6.4-12.20240127.fc41 fedora 562 k Installing dependencies: libedit-devel x86_64 3.1-54.20250104cvs.fc41 updates 41 k lld-libs x86_64 18.1.8-1.fc41 updates 1.5 M llvm-googletest x86_64 18.1.8-4.fc41 updates 382 k llvm-static x86_64 18.1.8-4.fc41 updates 36 M llvm-test x86_64 18.1.8-4.fc41 updates 648 k ncurses-c++-libs x86_64 6.4-12.20240127.fc41 fedora 38 k Transaction Summary ======================================================================================================================================== Install 12 Packages Total download size: 71 M Installed size: 409 M Is this ok [y/N]: Installed: clang-18.1.8-2.fc41.x86_64 libedit-devel-3.1-54.20250104cvs.fc41.x86_64 lld-18.1.8-1.fc41.x86_64 lld-devel-18.1.8-1.fc41.x86_64 lld-libs-18.1.8-1.fc41.x86_64 llvm-18.1.8-4.fc41.x86_64 llvm-devel-18.1.8-4.fc41.x86_64 llvm-googletest-18.1.8-4.fc41.x86_64 llvm-static-18.1.8-4.fc41.x86_64 llvm-test-18.1.8-4.fc41.x86_64 ncurses-c++-libs-6.4-12.20240127.fc41.x86_64 ncurses-devel-6.4-12.20240127.fc41.x86_64 Complete!
Po těchto krocích doinstalujeme ještě několik dalších knihoven (ve vývojové verzi), které mohou být využity moduly programovacího jazyka C3. Instalace těchto balíčků sice není nezbytně nutná, ovšem jejich neexistence omezí možnosti programů psaných v C3:
$ sudo dnf install libcurl-devel zlib-devel libzstd-devel libxml2-devel libffi-devel
Z výpisu je opět patrné, že některé z těchto balíčků již byly v systému nainstalovány:
Last metadata expiration check: 0:15:55 ago on Fri 29 Aug 2025 11:36:33 AM CEST. Package zlib-ng-compat-devel-2.1.7-2.fc41.x86_64 is already installed. Package libzstd-devel-1.5.7-1.fc41.x86_64 is already installed. Package libxml2-devel-2.12.10-1.fc41.x86_64 is already installed. Package libffi-devel-3.4.4-7.fc41.x86_64 is already installed. Dependencies resolved. ======================================================================================================================================== Package Architecture Version Repository Size ======================================================================================================================================== Installing: libcurl-devel x86_64 8.6.0-10.fc41 updates 851 k Transaction Summary ======================================================================================================================================== Install 1 Package Total download size: 851 k Installed size: 1.2 M Is this ok [y/N]: Installed: libcurl-devel-8.6.0-10.fc41.x86_64 Complete!
3. Překlad C3
Nyní, když máme v operačním systému nainstalovány všechny potřebné nástroje i knihovny, můžeme provést druhý důležitý krok. Tím je překlad překladače (sic!) i dalších nástrojů ekosystému programovacího jazyka C3. Nejprve naklonujeme repositář se zdrojovými kódy C3, což je snadné (je vyžadován nástroj Git, který jsme nainstalovali v rámci předchozí kapitoly):
$ git clone https://github.com/c3lang/c3c.git
Výsledkem by měl být nový adresář pojmenovaný – jak jinak – c3c:
Cloning into 'c3c'... remote: Enumerating objects: 52579, done. remote: Counting objects: 100% (173/173), done. remote: Compressing objects: 100% (97/97), done. remote: Total 52579 (delta 107), reused 81 (delta 74), pack-reused 52406 (from 3) Receiving objects: 100% (52579/52579), 18.51 MiB | 10.34 MiB/s, done. Resolving deltas: 100% (38876/38876), done.
Následně do tohoto adresáře přejdeme a zavoláme cmake (další již nainstalovaný nástroj) pro konfiguraci celého procesu překladu:
$ cd c3c $ cmake -B build -S . -DC3_LINK_DYNAMIC=1 -- Output to: "/tmp/ramdisk/c3c/build" -- Configuring done (1.7s) -- Generating done (0.0s) -- Build files have been written to: /tmp/ramdisk/c3c/build
Následuje poslední příkaz, který provede celý překlad:
$ cmake --build build [ 1%] Building C object CMakeFiles/miniz.dir/dependencies/miniz/miniz.c.o [ 2%] Linking C static library libminiz.a [ 2%] Built target miniz [ 3%] Building CXX object CMakeFiles/c3c_wrappers.dir/wrapper/src/wrapper.cpp.o [ 4%] Linking CXX static library libc3c_wrappers.a [ 4%] Built target c3c_wrappers [ 5%] Generating git_hash.h Git Hash: ca2fabc9f9cf511f1abf671b95e6f058ec512f5a [ 6%] Building C object CMakeFiles/c3c.dir/src/build/builder.c.o ... ... ... [ 96%] Building C object CMakeFiles/c3c.dir/src/compiler/llvm_codegen_value.c.o [ 97%] Building C object CMakeFiles/c3c.dir/src/compiler/llvm_codegen_storeload.c.o [ 98%] Building C object CMakeFiles/c3c.dir/src/compiler/llvm_codegen_builtins.c.o [100%] Linking CXX executable c3c [100%] Built target c3c
Výsledek by měl být uložen do podadresáře nazvaného build, ve kterém (kromě dalších souborů) nalezneme i spustitelný soubor nazvaný c3c. Ten použijeme v dalším textu:
$ cd build $ ls -l total 25664 -rwxr-xr-x. 1 ptisnovs ptisnovs 14267984 Aug 29 11:55 c3c -rw-r--r--. 1 ptisnovs ptisnovs 23222 Aug 29 11:54 CMakeCache.txt drwxr-xr-x. 11 ptisnovs ptisnovs 380 Aug 29 11:55 CMakeFiles -rw-r--r--. 1 ptisnovs ptisnovs 3232 Aug 29 11:54 cmake_install.cmake -rw-r--r--. 1 ptisnovs ptisnovs 73 Aug 29 11:55 git_hash.h drwxr-xr-x. 3 ptisnovs ptisnovs 60 Aug 29 11:54 lib -rw-r--r--. 1 ptisnovs ptisnovs 11505170 Aug 29 11:55 libc3c_wrappers.a -rw-r--r--. 1 ptisnovs ptisnovs 368636 Aug 29 11:54 libminiz.a -rw-r--r--. 1 ptisnovs ptisnovs 99155 Aug 29 11:54 Makefile
4. První spuštění překladače
Po (doufejme, že úspěšném) překladu se již můžeme pokusit o spuštění překladače programovacího jazyka C3. Pokud se stále nacházíte v adresáři c3/build, postačuje spustit c3c právě z tohoto adresáře. Z výpisu dostupných příkazů je patrné, že i přes své jméno není c3c „pouhým“ překladačem, ale integruje v sobě i mnohé další nástroje (tím se do jisté míry podobá nástroji go pro stejnojmenný programovací jazyk):
$ ./c3c
Výpis by měl vypadat následovně:
Usage: ./c3c [<options>] <command> [<args>] Commands: compile <file1> [<file2> ...] Compile files without a project into an executable. init <project name> Initialize a new project structure. init-lib <library name> Initialize a new library structure. build [<target>] Build the target in the current project. benchmark [<target>] Run the benchmarks for the target in the current project. test [<target>] Run the unit tests for the target in the current project. clean Clean all build files. run [<target>] [-- [<arg1> ...]] Run (and build if needed) the target in the current project. dist [<target>] Clean and build a target for distribution. clean-run [<target>] [-- [<arg1> ...]] Clean, then run the target. compile-run <file1> [<file2> ...] [-- [<arg1> ...]] Compile files then immediately run the result. compile-only <file1> [<file2> ...] Compile files but do not perform linking. compile-benchmark <file1> [<file2> ...] Compile files into a test-executable and run benchmarks. compile-test <file1> [<file2> ...] Compile files into a benchmark-executable and run unit tests. static-lib <file1> [<file2> ...] Compile files without a project into a static library. dynamic-lib <file1> [<file2> ...] Compile files without a project into a dynamic library. vendor-fetch <library> ... Fetches one or more libraries from the vendor collection. project <subcommand> ... Manipulate or view project files. Common options: -h -hh --help - Print the help, -h for the normal options, -hh for the full help. -V --version - Print version information. -q --quiet - Silence unnecessary output. -v -vv -vvv - Verbose output, -v for default, -vv and -vvv gives more information. -E - Lex only. -P - Only parse and output the AST as JSON. -C - Only lex, parse and check. - - Read code from standard in. -o <file> - Write output to <file>. -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. -D <name> - Add feature flag <name>. -U <name> - Remove feature flag <name>. --about - Prints a short description of C3. --build-env - Prints build environment information (only valid with in combination with a command such as 'compile'). --run-dir <dir> - Set the directory from where to run the binary (only for run and compile-run). --libdir <dir> - Add this directory to the c3l library search paths. --lib <name> - Add this c3l library to the compilation. --sources <file1> [<file2> ...] - Add these additional sources to the compilation. -g - Emit debug info. -g0 - Emit no debug info. -l <library> - Link with the static or dynamic library provided. -L <library dir> - Append the directory to the linker search paths. -z <argument> - Send the <argument> as a parameter to the linker. Use --help or -hh to view the full list of options.
5. Je jazyk C3 zpětně kompatibilní s jazykem C?
Na základě jména C3 i toho, že se na úvodní stránce tohoto jazyka píše „the C-like for programmers who like C“, bychom mohli nabýt dojmu, že je tento jazyk po syntaktické a sémantické stránce s původním céčkem kompatibilní (například tak, jako první verze jazyka C++). Ve skutečnosti tomu tak ovšem není (jde o C-like jazyk), o čemž se můžeme snadno přesvědčit pokusem o překlad libovolného céčkovského zdrojového kódu překladačem c3c. Konkrétně použijeme tento zdrojový kód, na který ještě později navážeme. Pokus o překlad dopadne takto:
1: #include <stdlib.h>
^^^^^^^^
(/tmp/ramdisk/c3c/build/renderer.c3:1:1) Error: Expected the start of a global declaration here.
2: #include <string.h>
3: #include <stdio.h>
4:
5: #define NULL_CHECK(value) \
^
(/tmp/ramdisk/c3c/build/renderer.c3:5:80) Error: '\' may not be placed outside of a string or comment, did you perhaps forget a " somewhere?
3: #include <stdio.h>
4:
5: #define NULL_CHECK(value) \
6: if (value == NULL) { \
^
(/tmp/ramdisk/c3c/build/renderer.c3:6:80) Error: '\' may not be placed outside of a string or comment, did you perhaps forget a " somewhere?
4:
5: #define NULL_CHECK(value) \
6: if (value == NULL) { \
7: fprintf(stderr, "NULL parameter: %s\n", #value); \
^
(/tmp/ramdisk/c3c/build/renderer.c3:7:80) Error: '\' may not be placed outside of a string or comment, did you perhaps forget a " somewhere?
5: #define NULL_CHECK(value) \
6: if (value == NULL) { \
7: fprintf(stderr, "NULL parameter: %s\n", #value); \
8: return; \
^
(/tmp/ramdisk/c3c/build/renderer.c3:8:80) Error: '\' may not be placed outside of a string or comment, did you perhaps forget a " somewhere?
6. Porovnání syntaxe jazyka C s dalšími mainstreamovými programovacími jazyky
Jak jsme si již řekli v úvodní kapitole, patří programovací jazyk C3 do rozsáhlé rodiny takzvaných „céčkovských“ programovacích jazyků, tj. jazyků více či méně inspirovaných jazykem C (typicky ANSI C, pozdější verze C jsou již do jisté míry specifické). Asi nejznámějším příkladem je jazyk C++, který byl dokonce ve svých prvních verzích s céčkem kompatibilní. Podobnost mezi céčkovskými jazyky není pouze sémantická, ale můžeme zde vidět i značnou syntaktickou podobnost.
Ostatně si můžeme ukázat, jak se liší či naopak podobají zápisy stejných algoritmů v různých jazycích z rodiny C. Pro jednoduchost zůstaneme u klasického „školního“ příkladu – výpočtu faktoriálu celého (kladného) čísla. Nerekurzivní varianta psaná v C vypadá takto:
int factorial(int n) {
int result = 1;
for (int i = 1; i <= n; ++i) {
result *= i;
}
return result;
}
Rekurzivní variantu lze zapsat například takto (plná rekurze):
int factorial_recursive(int n) {
return n == 0 ? 1 : n * factorial_recursive(n - 1);
}
V jazyce C3 bude zápis jen nepatrně odlišný (viz klíčové slovo fn):
fn int factorial(int n)
{
int result = 1;
for (int i = 1; i <= n; ++i)
{
result *= i;
}
return result;
}
Rekurzivní varianta:
fn int factorial_recursive(int n)
{
return n == 0 ? 1 : n * factorial_recursive(n - 1);
}
Dalším céčkovským jazykem je jazyk V, ve kterém bude zápis vypadat takto (bez středníků):
fn factorial_recursive(n int) int {
if n == 0 {
return 1
}
return n * factorial_recursive(n - 1)
}
V programovacím jazyku Zig se taktéž používá klíčové slovo fn. Změnili jsme i datové typy – nyní striktně bezznaménkové:
fn factorial_recursive(x: u32) u32 {
if (x == 0) {
return 1;
} else {
return factorial_recursive(x - 1) * x;
}
}
Následuje zápis obou algoritmů v jazyce Go.
Nerekurzivní varianta:
func factorial(n uint) uint {
if n == 0 {
return 1
}
result := 1
for i := 1; i <= n; i++ {
result *= i
}
return result
}
Rekurzivní varianta:
func factorial_recursive(n uint64)(result uint64) {
if (n > 0) {
result = n * factorial_rerursive(n-1)
return result
}
return 1
}
A konečně se podívejme na jazyk Rust, ve kterém se již rozlišují měnitelné a neměnitelné hodnoty:
fn factorial(number : u32) -> u32{
let mut factorial : u32 = 1;
for i in 1..(number+1) {
factorial*=i;
}
return factorial
}
fn factorial_recursive(number : u32) -> u32{
if number<=1 {
return 1;
}
return number * factorial_recursive(number-1);
}
7. První reálný program vytvořený v jazyku C3
V předchozí kapitole byly ukázány syntaktické rozdíly mezi různými jazyky odvozenými od klasického céčka. Mj. jsme si ukázali i část zdrojového kódu napsaného přímo v jazyku C3. Ovšem nejednalo se o ucelený kód, takže si ho nyní rozšiřme. Pod tímto odstavcem je uveden celý (a přeložitelný) zdrojový kód, po jehož spuštění se vypíše tabulka faktoriálů pro vstupní hodnoty od 0 do 10 (včetně). Povšimněte si zejména způsobu definice funkce klíčovým slovem fn, dále importu modulu std::io (nejvíce diskutovaný rozdíl oproti C) a volání funkce printf z modulu io:
import std::io;
fn int factorial(int n)
{
int result = 1;
for (int i = 1; i <= n; ++i)
{
result *= i;
}
return result;
}
fn void main()
{
for (int n = 0; n <= 10; n++)
{
io::printf("%d! = %d\n", n, factorial(n));
}
}
Překlad a následně spuštění programu provedené ve dvou krocích:
$ ./c3c compile factorial.c3 Program linked to executable './factorial'. $ ./factorial 0! = 1 1! = 1 2! = 2 3! = 6 4! = 24 5! = 120 6! = 720 7! = 5040 8! = 40320 9! = 362880 10! = 3628800
Překlad a následné spuštění programu v jednom kroku:
$ ./c3c compile-run factorial.c3 Program linked to executable './factorial'. Launching ./factorial 0! = 1 1! = 1 2! = 2 3! = 6 4! = 24 5! = 120 6! = 720 7! = 5040 8! = 40320 9! = 362880 10! = 3628800
8. Makra v programovacím jazyku C3
Poměrně specifickým rysem jazyka C3 je jeho podpora pro tvorbu maker a pochopitelně i pro jejich expanzi v čase překladu. Na rozdíl od (poněkud zjednodušeně řečeno) textových substitucí prováděných preprocesorem jazyka C jsou makra v C3 navržena takovým způsobem, aby jejich tvorba i volání (resp. expanze) byly prováděny bezpečněji. Makra tedy mají plnohodnotné parametry, které lze v textu makra použít bez nutnosti jejich uzávorkování. Taktéž je možné specifikovat typ návratové hodnoty makra atd. Ostatně bez dalších podrobnějších popisů (tomu bude věnován samostatný text) si ukažme makro pro výpočet faktoriálu. To je expandováno již v čase překladu:
import std::io;
macro int factorial($n)
{
$if $n == 0:
return 1;
$else
return $n * factorial($n - 1);
$endif
}
fn void main()
{
const int N = 10;
io::printf("%d! = %d\n", N, factorial(N));
}
Překlad programu s jeho spuštěním:
$ ./c3c compile-run factorial_macro.c3 Program linked to executable './factorial_macro'. Launching ./factorial_macro 10! = 3628800 Program completed with exit code 0.
9. Od C k C3
Dnes nebudeme podrobně popisovat ani syntaxi ani sémantiku jazyka C3. Zvolíme spíše prakticky zaměřený přístup. Konkrétně se pokusíme o transformaci následujícího zdrojového kódu napsaného v ANSI C do jazyka C3. Tento kód po svém spuštění vygeneruje rastrový obrázek s vypočtenou Juliovou množinou:
Povšimněte si, že se ve zdrojovém kódu používají struktury, ukazatele na struktury, základní příkazy pro řízení toku programu (podmínky a smyčky), ukazatele, makra, dynamická alokace paměti a v neposlední řadě taktéž standardní knihovní funkce pro tisk na standardní výstup:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define NULL_CHECK(value) \
if (value == NULL) { \
fprintf(stderr, "NULL parameter: %s\n", #value); \
return; \
}
typedef struct {
unsigned int width;
unsigned int height;
} resolution_t;
void calc_julia(resolution_t *resolution, const unsigned int maxiter, const unsigned char *palette, double cx, double cy)
{
double zy0 = -1.5;
int y;
NULL_CHECK(palette);
puts("P3");
printf("%d %d\n", resolution->width, resolution->height);
puts("255");
for (y=0; y < resolution->height; y++) {
double zx0 = -1.5;
int x;
for (x=0; x < resolution->width; x++) {
double zx = zx0;
double zy = zy0;
unsigned int i = 0;
while (i < maxiter) {
double zx2 = zx * zx;
double zy2 = zy * zy;
if (zx2 + zy2 > 4.0) {
break;
}
zy = 2.0 * zx * zy + cy;
zx = zx2 - zy2 + cx;
i++;
}
{
const unsigned char *color = palette + 3*(i % 256);
unsigned char r = *color++;
unsigned char g = *color++;
unsigned char b = *color;
printf("%d %d %d\n", r, g, b);
}
zx0 += 3.0/resolution->width;
}
zy0 += 3.0/resolution->height;
}
}
/* Generate color palette: 256 colors, each color is represented as RGB triple. */
unsigned char *generate_palette(void) {
unsigned char *palette = (unsigned char *)malloc(256 * 3);
unsigned char *p = palette;
int i;
if (palette == NULL) {
return NULL;
}
/* fill in by black color */
memset(palette, 0, 256 * 3);
/* green gradient */
for (i = 0; i < 32; i++) {
*p++ = 0;
*p++ = 4 + i*6;
*p++ = 0;
}
/* gradient from green to yellow */
for (i = 0; i < 32; i++) {
*p++ = 4 + i * 6;
*p++ = i * 2 < 52 ? 200 + i * 2 : 252;
*p++ = 0;
}
/* gradient from yellow to white */
for (i = 0; i < 32; i++) {
*p++ = i * 2 < 52 ? 200 + i * 2 : 252;
*p++ = 252;
*p++ = i * 6;
}
/* gradient from white to yellow */
for (i = 0; i < 48; i++) {
*p++ = 252;
*p++ = 252;
*p++ = 252 - i * 6;
}
/* gradient from yellow to green */
for (i = 0; i < 48; i++) {
*p++ = 252 - i * 6;
*p++ = 252;
*p++ = 0;
}
/* gradient green to black */
for (i = 0; i < 48; i++) {
*p++ = 0;
*p++ = 252 - i * 6;
*p++ = 0;
}
return palette;
}
int main(int argc, char **argv)
{
resolution_t resolution;
unsigned char *palette = generate_palette();
int maxiter;
if (argc < 4) {
puts("usage: ./mandelbrot width height maxiter");
return 1;
}
resolution.width = atoi(argv[1]);
resolution.height = atoi(argv[2]);
maxiter = atoi(argv[3]);
calc_julia(&resolution, maxiter, palette, -0.207190825, 0.676656625);
return 0;
}
10. Definice datové struktury s rozměry rastrového obrázku
Nejprve se podíváme na to, jakým způsobem se v jazyce C3 definuje datová struktura s popisem rastrového obrázku. V céčku je definice takové struktury následující:
typedef struct {
unsigned int width;
unsigned int height;
} resolution_t;
V jazyce C3 je sémantika prakticky stejná, liší se jen způsob zápisu (tedy syntaxe). Odlišují se i jména datových typů – namísto modifikátorů typu int (viz C) přímo použijeme datový typ uint:
struct Resolution {
uint width;
uint height;
}
6: struct resolution {
^^^^^^^^^^
(/tmp/ramdisk/c3c/build/renderer_v8.c1:6:8) Error: Names of structs must start with an uppercase letter.
11. Skeleton všech funkcí
Dále do první verze našeho programu dopíšeme skeleton všech tří funkcí, které později budeme implementovat. Povšimněte si, že parametry lze předávat jak hodnotou (maxiter, cx, cy), tak i odkazem (resolution, palette), tedy stejně, jako je tomu v jazyku C. A opět – odlišuje se jen syntaxe, která nyní vyžaduje použití klíčového slova fn na začátku hlaviček funkcí:
fn void calc_julia(Resolution *resolution, uint maxiter, char *palette, double cx, double cy) {
}
fn char *generate_palette() {
}
fn void main()
{
}
Ve skutečnosti by takový program nebylo možné přeložit, protože funkce generate_palette musí vracet nějakou hodnotu (klidně i null):
fn char *generate_palette() {
char *palette = null;
return palette;
}
První verze programu pro výpočet Juliovy množiny vypadá následovně:
struct Resolution {
uint width;
uint height;
}
fn void calc_julia(Resolution *resolution, uint maxiter, char *palette, double cx, double cy)
{
}
fn char *generate_palette() {
char *palette = null;
return palette;
}
fn void main()
{
calc_julia(null, 0, null, 0.0, 0.0);
}
12. Anotace parametrů funkcí
V programovacím jazyku C je možné specifikovat, že některé parametry funkce nebudou uvnitř funkce modifikovány. K tomuto účelu se používá klíčové slovo const (u ukazatelů si musíme dát pozor na to, jestli je neměnný ukazatel nebo hodnota, na kterou ukazuje):
void calc_julia(resolution_t *resolution, const unsigned int maxiter, const unsigned char *palette, double cx, double cy)
V jazyku C3 se používá poněkud odlišný přístup. U parametrů typu ukazatel lze s využitím anotací specifikovat, jestli se může (přes předaný ukazatel) provádět zápis, nebo je paměťová oblast určena jen pro čtení (či pro obě operace). Vzhledem k tomu, že ve funkci calc_julia se přes oba ukazatele přistupuje ke struktuře resp. k poli, ze kterého se pouze provádí čtení, můžeme u obou těchto ukazatelů specifikovat příslušnou anotaci. Zápis je na první pohled poněkud zvláštní:
<*
@param [in] resolution
@param [in] palette
*>
fn void calc_julia(Resolution *resolution, uint maxiter, char *palette, double cx, double cy)
{
}
V případě, že se přes ukazatel pokusíme o zápis, povede to k chybě při překladu:
<*
@param [in] resolution
@param [in] palette
*>
fn void calc_julia(Resolution *resolution, uint maxiter, char *palette, double cx, double cy)
{
resolution.width=100;
}
Překladač nahlásí chybu:
11: *>
12: fn void calc_julia(Resolution *resolution, uint maxiter, char *palette, double cx, double cy)
13: {
14: resolution.width=100;
^^^^^^^^^^
(/tmp/ramdisk/c3c/build/renderer_v2.c3:14:5) Error: 'in' parameters may not be assigned to.
Druhá varianta demonstračního příkladu bude vypadat takto:
struct Resolution {
uint width;
uint height;
}
<*
@param [in] resolution
@param [in] palette
*>
fn void calc_julia(Resolution *resolution, uint maxiter, char *palette, double cx, double cy)
{
}
fn char *generate_palette() {
char *palette = null;
return palette;
}
fn void main()
{
calc_julia(null, 0, null, 0.0, 0.0);
}
13. Statická kontrola, zda se nepředávají neinicializované ukazatele
Překladač jazyka C3 dokáže provádět (i když prozatím dosti primitivním způsobem) statickou kontrolu, zda se do funkce nepředává neinicializovaný ukazatel. Prozatím umíme specifikovat, že se přes ukazatel nesmí provádět zápisy:
<*
@param [in] resolution
@param [in] palette
*>
fn void calc_julia(Resolution *resolution, uint maxiter, char *palette, double cx, double cy)
{
}
Pokud navíc v anotaci před slovo in vložíme znak ampersandu, znamená to, že ukazatel musí být inicializovaný a nesmí obsahovat hodnotu null:
<*
@param [&in] resolution
@param [&in] palette
*>
fn void calc_julia(Resolution *resolution, uint maxiter, char *palette, double cx, double cy)
{
}
Podívejme se nyní na upravený zdrojový kód ukázkového příkladu, v němž je použita upravená anotace ukazatelů předávaných do funkce calc_julia:
struct Resolution {
uint width;
uint height;
}
<*
@param [&in] resolution
@param [&in] palette
*>
fn void calc_julia(Resolution *resolution, uint maxiter, char *palette, double cx, double cy)
{
}
fn char *generate_palette() {
char *palette = null;
return palette;
}
fn void main()
{
calc_julia(null, 0, null, 0.0, 0.0);
}
Při pokusu o překlad vypíše překladač programovacího jazyka C3 chybu, protože při volání funkce calc_julia do ní skutečně předáváme hodnotu null:
$ ./c3c compile renderer_v3.c3
20:
21: fn void main()
22: {
23: calc_julia(null, 0, null, 0.0, 0.0);
^^^^
(/tmp/ramdisk/c3c/build/renderer_v3.c3:23:16) Error: You may not pass null to the '&' parameter.
9: @param [&in] resolution
10: @param [&in] palette
11: *>
12: fn void calc_julia(Resolution *resolution, uint maxiter, char *palette, double cx, double cy)
^^^^^^^^^^
(/tmp/ramdisk/c3c/build/renderer_v3.c3:12:9) Note: The definition is here.
14. Kontrola neinicializovaných ukazatelů v čase běhu programu
Mohlo by se zdát, že kontrola neinicializovaných ukazatelů v době překladu aplikace pokryje i následující situaci: stále vyžadujeme, aby byly do funkce calc_julia předávány ukazatele nastavené na nějakou adresu (a ne na none):
<*
@param [&in] resolution
@param [&in] palette
*>
fn void calc_julia(Resolution *resolution, uint maxiter, char *palette, double cx, double cy)
{
}
Dále ve funkci generate_palette evidentně vracíme hodnotu null:
fn char *generate_palette() {
char *palette = null;
return palette;
}
Tuto hodnotu následně předáváme do funkce calc_julia:
char* palette = generate_palette(); calc_julia(&resolution, 0, palette, 0.0, 0.0);
Mohli bychom předpokládat, že v tomto případě překladač jazyka C3 problém odhalí již v době překladu, ale ve skutečnosti tomu tak nebude – k chybě dojde až v době běhu.
Úplný zdrojový kód takto upraveného demonstračního příkladu bude vypadat následovně:
struct Resolution {
uint width;
uint height;
}
<*
@param [&in] resolution
@param [&in] palette
*>
fn void calc_julia(Resolution *resolution, uint maxiter, char *palette, double cx, double cy)
{
}
fn char *generate_palette() {
char *palette = null;
return palette;
}
fn void main()
{
Resolution resolution;
resolution.width = 512;
resolution.height = 512;
char* palette = generate_palette();
calc_julia(&resolution, 0, palette, 0.0, 0.0);
}
Tento zdrojový kód se bez problémů přeloží:
$ ./c3c compile renderer_v4.c3 Program linked to executable './renderer_v4'.
Po spuštění dojde k detekci chyby, a to až při volání funkce calc_julia:
$ ./renderer_v4 ERROR: 'Reference parameter 'palette' was passed a null pointer argument.' in std.core.builtin.default_panic (/tmp/ramdisk/c3c/lib/std/core/builtin.c3:175) [./renderer_v4] in renderer_v4.calc_julia (/tmp/ramdisk/c3c/build/renderer_v4.c3:12) [./renderer_v4] in renderer_v4.main (/tmp/ramdisk/c3c/build/renderer_v4.c3:29) [./renderer_v4] in @main_to_void_main (/tmp/ramdisk/c3c/lib/std/core/private/main_stub.c3:18) [./renderer_v4] [inline] in main (/tmp/ramdisk/c3c/build/renderer_v4.c3:21) [./renderer_v4] 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) [./renderer_v4] Illegal instruction (core dumped)
15. Přepis funkce pro inicializaci palety a striktnější typová kontrola
Pokusme se nyní do našeho postupně vznikajícího programu doplnit kód funkce generate_palette. Pro alokaci paměti použijeme funkci nazvanou malloc a pro vyplnění pole nulami pak funkci libc::memset (malloc není zapotřebí importovat – je přítomna již ve výchozím jmenném prostoru, ovšem její parametry jsou od céčka odlišné):
char *palette = malloc(256*3); /* fill in by black color */ libc::memset(palette, 0, 256 * 3);
Zatímco v případě jazyka C bylo možné pro naplnění palety – pole bajtů – použít výpočty s hodnotami typu int, v jazyce C3 tomu bude poněkud jinak. O tom se můžeme poměrně snadno přesvědčit:
import std::io;
import libc;
struct Resolution {
uint width;
uint height;
}
<*
@param [&in] resolution
@param [&in] palette
*>
fn void calc_julia(Resolution *resolution, uint maxiter, char *palette, double cx, double cy)
{
}
fn char *generate_palette() {
char *palette = malloc(256*3);
char *p = palette;
/* fill in by black color */
libc::memset(palette, 0, 256 * 3);
/* green gradient */
for (int i = 0; i < 32; i++) {
*p++ = 0;
*p++ = 4 + i*6;
*p++ = 0;
}
/* gradient from green to yellow */
for (int i = 0; i < 32; i++) {
*p++ = 4 + i * 6;
*p++ = i * 2 < 52 ? 200 + i * 2 : 252;
*p++ = 0;
}
/* gradient from yellow to white */
for (int i = 0; i < 32; i++) {
*p++ = i * 2 < 52 ? 200 + i * 2 : 252;
*p++ = 252;
*p++ = i * 6;
}
/* gradient from white to yellow */
for (int i = 0; i < 48; i++) {
*p++ = 252;
*p++ = 252;
*p++ = 252 - i * 6;
}
/* gradient from yellow to green */
for (int i = 0; i < 48; i++) {
*p++ = 252 - i * 6;
*p++ = 252;
*p++ = 0;
}
/* gradient green to black */
for (int i = 0; i < 48; i++) {
*p++ = 0;
*p++ = 252 - i * 6;
*p++ = 0;
}
return palette;
}
fn void main()
{
Resolution resolution;
resolution.width = 512;
resolution.height = 512;
char* palette = generate_palette();
calc_julia(&resolution, 0, palette, 0.0, 0.0);
}
Překladač jazyka C3 nás upozorní na to, že přes ukazatel typu char * není možné zapisovat hodnoty typu int. V tomto ohledu je tedy C3 přísnější, i když ne do takové míry, jako v případě jazyka Go:
24: /* green gradient */
25: for (int i = 0; i < 32; i++) {
26: *p++ = 0;
27: *p++ = 4 + i*6;
^
(/tmp/ramdisk/c3c/build/renderer_v5.c3:27:20) Error: 'int' cannot implicitly be converted to 'char', but you may use a cast.
30:
31: /* gradient from green to yellow */
32: for (int i = 0; i < 32; i++) {
33: *p++ = 4 + i * 6;
^
(/tmp/ramdisk/c3c/build/renderer_v5.c3:33:20) Error: 'int' cannot implicitly be converted to 'char', but you may use a cast.
31: /* gradient from green to yellow */
32: for (int i = 0; i < 32; i++) {
33: *p++ = 4 + i * 6;
34: *p++ = i * 2 < 52 ? 200 + i * 2 : 252;
^
(/tmp/ramdisk/c3c/build/renderer_v5.c3:34:35) Error: 'int' cannot implicitly be converted to 'char', but you may use a cast.
37:
38: /* gradient from yellow to white */
39: for (int i = 0; i < 32; i++) {
40: *p++ = i * 2 < 52 ? 200 + i * 2 : 252;
^
(/tmp/ramdisk/c3c/build/renderer_v5.c3:40:35) Error: 'int' cannot implicitly be converted to 'char', but you may use a cast.
39: for (int i = 0; i < 32; i++) {
40: *p++ = i * 2 < 52 ? 200 + i * 2 : 252;
41: *p++ = 252;
42: *p++ = i * 6;
^
(/tmp/ramdisk/c3c/build/renderer_v5.c3:42:16) Error: 'int' cannot implicitly be converted to 'char', but you may use a cast.
46: for (int i = 0; i < 48; i++) {
47: *p++ = 252;
48: *p++ = 252;
49: *p++ = 252 - i * 6;
^
(/tmp/ramdisk/c3c/build/renderer_v5.c3:49:22) Error: 'int' cannot implicitly be converted to 'char', but you may use a cast.
51:
52: /* gradient from yellow to green */
53: for (int i = 0; i < 48; i++) {
54: *p++ = 252 - i * 6;
^
(/tmp/ramdisk/c3c/build/renderer_v5.c3:54:22) Error: 'int' cannot implicitly be converted to 'char', but you may use a cast.
59: /* gradient green to black */
60: for (int i = 0; i < 48; i++) {
61: *p++ = 0;
62: *p++ = 252 - i * 6;
^
(/tmp/ramdisk/c3c/build/renderer_v5.c3:62:22) Error: 'int' cannot implicitly be converted to 'char', but you may use a cast.
16. Výpočty barev v barvové paletě s hodnotami typu char a nikoli int
Aby bylo možné funkci pro výpočet barvové palety přeložit, musíme použít buď explicitní přetypování nebo pozměnit veškeré výpočty takovým způsobem, aby se v nich používaly pouze hodnoty typu char. Tedy například namísto:
/* green gradient */
for (int i = 0; i < 32; i++) {
*p++ = 0;
*p++ = 4 + i*6;
*p++ = 0;
}
programovou smyčku upravíme do podoby:
/* green gradient */
for (char i = 0; i < 32; i++) {
*p++ = 0;
*p++ = 4 + i*6;
*p++ = 0;
}
Výsledný zdrojový kód bude vypadat následovně:
import std::io;
import libc;
struct Resolution {
uint width;
uint height;
}
<*
@param [&in] resolution
@param [&in] palette
*>
fn void calc_julia(Resolution *resolution, uint maxiter, char *palette, double cx, double cy)
{
}
fn char *generate_palette() {
char *palette = malloc(256*3);
char *p = palette;
/* fill in by black color */
libc::memset(palette, 0, 256 * 3);
/* green gradient */
for (char i = 0; i < 32; i++) {
*p++ = 0;
*p++ = 4 + i*6;
*p++ = 0;
}
/* gradient from green to yellow */
for (char i = 0; i < 32; i++) {
*p++ = 4 + i * 6;
*p++ = i * 2 < 52 ? 200 + i * 2 : 252;
*p++ = 0;
}
/* gradient from yellow to white */
for (char i = 0; i < 32; i++) {
*p++ = i * 2 < 52 ? 200 + i * 2 : 252;
*p++ = 252;
*p++ = i * 6;
}
/* gradient from white to yellow */
for (char i = 0; i < 48; i++) {
*p++ = 252;
*p++ = 252;
*p++ = 252 - i * 6;
}
/* gradient from yellow to green */
for (char i = 0; i < 48; i++) {
*p++ = 252 - i * 6;
*p++ = 252;
*p++ = 0;
}
/* gradient green to black */
for (char i = 0; i < 48; i++) {
*p++ = 0;
*p++ = 252 - i * 6;
*p++ = 0;
}
return palette;
}
fn void main()
{
Resolution resolution;
resolution.width = 512;
resolution.height = 512;
char* palette = generate_palette();
calc_julia(&resolution, 0, palette, 0.0, 0.0);
}
Takto upravený zdrojový kód již bude bez problémů přeložitelný.
17. Volání knihovní I/O funkce a volání nativní céčkovské I/O funkce
Jednou z předností programovacího jazyka C3 je fakt, že je plně podporováno volání nativních céčkovských funkcí, tedy například funkcí ze standardní knihovny atd. Navíc jazyk C3 obsahuje i vlastní knihovní funkce, které lze do jisté míry „mixovat“ s funkcemi céčkovskými. Týká se to i vstupně-výstupního systému, tedy například funkcí určených pro tisk hodnot na standardní výstup atd. Příkladem může být tisk hodnot známou céčkovskou funkcí puts (tisk řetězce) a funkcí io::printf ze standardní knihovny jazyka C3 (jak je z názvu této funkce patrné, je částečně odvozena od své céčkovské varianty, ovšem jedná se o odlišnou implementaci):
import std::io;
extern fn int puts(char*);
fn void calc_julia(Resolution *resolution, uint maxiter, char *palette, double cx, double cy)
{
puts("P3");
io::printf("%d %d\n", resolution.width, resolution.height);
puts("255");
}
Výše uvedený fragment kódu by měl po svém zavolání vytisknout hlavičku rastrového obrázku uloženého ve formátu PPM (Portable Pixmap Format, viz též Grafické formáty ve znamení Unixu):
P3 512 512 255
Náš demonstrační příklad nyní upravíme do takové podoby, aby po svém spuštění alespoň provedl tisk hlavičky rastrového obrázku (prozatím bez obsahu):
import std::io;
import libc;
extern fn int puts(char*);
struct Resolution {
uint width;
uint height;
}
<*
@param [&in] resolution
@param [&in] palette
*>
fn void calc_julia(Resolution *resolution, uint maxiter, char *palette, double cx, double cy)
{
puts("P3");
io::printf("%d %d\n", resolution.width, resolution.height);
puts("255");
}
fn char *generate_palette() {
char *palette = malloc(256*3);
char *p = palette;
/* fill in by black color */
libc::memset(palette, 0, 256 * 3);
/* green gradient */
for (char i = 0; i < 32; i++) {
*p++ = 0;
*p++ = 4 + i*6;
*p++ = 0;
}
/* gradient from green to yellow */
for (char i = 0; i < 32; i++) {
*p++ = 4 + i * 6;
*p++ = i * 2 < 52 ? 200 + i * 2 : 252;
*p++ = 0;
}
/* gradient from yellow to white */
for (char i = 0; i < 32; i++) {
*p++ = i * 2 < 52 ? 200 + i * 2 : 252;
*p++ = 252;
*p++ = i * 6;
}
/* gradient from white to yellow */
for (char i = 0; i < 48; i++) {
*p++ = 252;
*p++ = 252;
*p++ = 252 - i * 6;
}
/* gradient from yellow to green */
for (char i = 0; i < 48; i++) {
*p++ = 252 - i * 6;
*p++ = 252;
*p++ = 0;
}
/* gradient green to black */
for (char i = 0; i < 48; i++) {
*p++ = 0;
*p++ = 252 - i * 6;
*p++ = 0;
}
return palette;
}
fn void main()
{
Resolution resolution;
resolution.width = 512;
resolution.height = 512;
char* palette = generate_palette();
calc_julia(&resolution, 0, palette, 0.0, 0.0);
}
18. Funkční varianta céčkovského programu z deváté kapitoly
Náš program pro výpočet Juliovy množiny je téměř kompletní. Zbývám nám dodat „pouze“ vlastní výpočet, což sice může vypadat komplikovaně, ale tento výpočet se prakticky vůbec nebude lišit od původní céčkovské varianty, takže můžeme programovat dnes tak oblíbeným stylem Copy&Paste :-):
fn void calc_julia(Resolution *resolution, uint maxiter, char *palette, double cx, double cy)
{
puts("P3");
io::printf("%d %d\n", resolution.width, resolution.height);
puts("255");
double zy0 = -1.5;
int y;
for (y=0; y < resolution.height; y++) {
double zx0 = -1.5;
int x;
for (x=0; x < resolution.width; x++) {
double zx = zx0;
double zy = zy0;
uint i = 0;
while (i < maxiter) {
double zx2 = zx * zx;
double zy2 = zy * zy;
if (zx2 + zy2 > 4.0) {
break;
}
zy = 2.0 * zx * zy + cy;
zx = zx2 - zy2 + cx;
i++;
}
char *color = palette + 3*(i % 256);
char r = *color++;
char g = *color++;
char b = *color;
io::printf("%d %d %d\n", r, g, b);
zx0 += 3.0/resolution.width;
}
zy0 += 3.0/resolution.height;
}
}
Porovnání původní céčkovské varianty a varianty naprogramované v jazyku C3 řádek po řádku:
void calc_julia(resolution_t *resolution, const unsigned int ... fn void calc_julia(Resolution *resolution, uint maxiter, char *palette, double cx, double cy)
{ {
double zy0 = -1.5; double zy0 = -1.5;
int y; int y;
puts("P3"); puts("P3");
printf("%d %d\n", resolution->width, resolution->height); io::printf("%d %d\n", resolution.width, resolution.height);
puts("255"); puts("255");
for (y=0; y<resolution->height; y++) { for (y=0; y<resolution.height; y++) {
double zx0 = -1.5; double zx0 = -1.5;
int x; int x;
for (x=0; x<resolution->width; x++) { for (x=0; x<resolution.width; x++) {
double zx = zx0; double zx = zx0;
double zy = zy0; double zy = zy0;
unsigned int i = 0; uint i = 0;
while (i < maxiter) { while (i < maxiter) {
double zx2 = zx * zx; double zx2 = zx * zx;
double zy2 = zy * zy; double zy2 = zy * zy;
if (zx2 + zy2 > 4.0) { if (zx2 + zy2 > 4.0) {
break; break;
} }
zy = 2.0 * zx * zy + cy; zy = 2.0 * zx * zy + cy;
zx = zx2 - zy2 + cx; zx = zx2 - zy2 + cx;
i++; i++;
} }
{
const unsigned char *color = palette + 3*(i % 256); char *color = palette + 3*(i % 256);
unsigned char r = *color++; char r = *color++;
unsigned char g = *color++; char g = *color++;
unsigned char b = *color; char b = *color;
printf("%d %d %d\n", r, g, b); io::printf("%d %d %d\n", r, g, b);
}
zx0 += 3.0/resolution->width; zx0 += 3.0/resolution.width;
} }
zy0 += 3.0/resolution->height; zy0 += 3.0/resolution.height;
} }
} }
Celý zdrojový kód plně funkčního demonstračního příkladu vypadá následovně:
import std::io;
import libc;
extern fn int puts(char*);
struct Resolution {
uint width;
uint height;
}
<*
@param [&in] resolution
@param [&in] palette
*>
fn void calc_julia(Resolution *resolution, uint maxiter, char *palette, double cx, double cy)
{
puts("P3");
io::printf("%d %d\n", resolution.width, resolution.height);
puts("255");
double zy0 = -1.5;
int y;
for (y=0; y < resolution.height; y++) {
double zx0 = -1.5;
int x;
for (x=0; x < resolution.width; x++) {
double zx = zx0;
double zy = zy0;
uint i = 0;
while (i < maxiter) {
double zx2 = zx * zx;
double zy2 = zy * zy;
if (zx2 + zy2 > 4.0) {
break;
}
zy = 2.0 * zx * zy + cy;
zx = zx2 - zy2 + cx;
i++;
}
char *color = palette + 3*(i % 256);
char r = *color++;
char g = *color++;
char b = *color;
io::printf("%d %d %d\n", r, g, b);
zx0 += 3.0/resolution.width;
}
zy0 += 3.0/resolution.height;
}
}
fn char *generate_palette() {
char *palette = malloc(256*3);
char *p = palette;
/* fill in by black color */
libc::memset(palette, 0, 256 * 3);
/* green gradient */
for (char i = 0; i < 32; i++) {
*p++ = 0;
*p++ = 4 + i*6;
*p++ = 0;
}
/* gradient from green to yellow */
for (char i = 0; i < 32; i++) {
*p++ = 4 + i * 6;
*p++ = i * 2 < 52 ? 200 + i * 2 : 252;
*p++ = 0;
}
/* gradient from yellow to white */
for (char i = 0; i < 32; i++) {
*p++ = i * 2 < 52 ? 200 + i * 2 : 252;
*p++ = 252;
*p++ = i * 6;
}
/* gradient from white to yellow */
for (char i = 0; i < 48; i++) {
*p++ = 252;
*p++ = 252;
*p++ = 252 - i * 6;
}
/* gradient from yellow to green */
for (char i = 0; i < 48; i++) {
*p++ = 252 - i * 6;
*p++ = 252;
*p++ = 0;
}
/* gradient green to black */
for (char i = 0; i < 48; i++) {
*p++ = 0;
*p++ = 252 - i * 6;
*p++ = 0;
}
return palette;
}
fn void main()
{
Resolution resolution;
resolution.width = 512;
resolution.height = 512;
char* palette = generate_palette();
calc_julia(&resolution, 1000, palette, -0.207190825, 0.676656625);
}
Po překladu a spuštění tohoto programu by se měl vypočítat a vykreslit následující rastrový obrázek:
19. Repositář s demonstračními příklady
Dnes ukázané demonstrační příklady 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):
| # | 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 |
20. Odkazy na Internetu
- 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

