Kombinace programovacího jazyka C3 s assemblerem

16. 12. 2025
Doba čtení: 59 minut

Sdílet

Programovací jazyk C3
Autor: Root.cz s využitím Zoner AI
V dnešní části seriálu o programovacím jazyku C3 se budeme zabývat tím, jakým způsobem je možné zkombinovat zdrojový kód v jazyku C3 s assemblerem. Jedná se přitom o jednu z klíčových vlastností programovacího jazyka C3.

Obsah

1. Kombinace programovacího jazyka C3 s assemblerem

2. Krátké ohlédnutí do minulosti

3. Assemblery v Linuxu

4. Assemblery vestavěné do vyšších programovacích jazyků

5. Assembler a programovací jazyk C

6. Assembler a programovací jazyk C3

7. Instrukce assembleru posílané přímo do výstupu pro backend překladač

8. Přístup k lokálním proměnným bez specifikace jejich jména

9. Kontrola, jakým způsobem byl překlad proveden – přepínač –emit-asm

10. Blok asm zabudovaný přímo do jazyka C3

11. Realizace volání služby operačního systému přes blok asm

12. Přístup k lokálním proměnným z bloku asm s využitím jejich jména

13. Jakým způsobem se předávají parametry do funkcí a jak se z nich vrací hodnoty?

14. Implementace celé funkce ve vestavěném assembleru?

15. Volání funkcí naprogramovaných v „externím“ assembleru

16. Krátké zopakování: volání externích funkcí naprogramovaných v jazyku C

17. Zavolání funkce naprogramované v assembleru z jazyka C3

18. Shrnutí

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

1. Kombinace programovacího jazyka C3 s assemblerem

V dnešní části seriálu o programovacím jazyku C3 se budeme zabývat tím, jakým způsobem je možné zkombinovat zdrojový kód v jazyku C3 s assemblerem. Jedná se o jednu z vlastností programovacího jazyka C3, která je zmíněna na domovské stránce tohoto jazyka jako jedna ze stěžejních technologií, která jazyk C3 odlišuje od standardního jazyka C.

Je ovšem nutné poznamenat, že tuto technologii v praxi využijí s velkou pravděpodobností pouze někteří vývojáři, a to navíc pouze ve specifických situacích (ruční vektorizace kódu, tvorba kodeků popř. subrutin pro zpracování signálů, volání specifických instrukcí typu generování náhodných čísel, kryptografických operací atd.). Na druhou stranu se ovšem jedná o téma, které velmi dobře odhaluje činnost dalších pomocných nástrojů, které jsou nedílnou součástí ekosystému tohoto programovacího jazyka. Zabývat se totiž budeme hned několika způsoby, jakými je možné zkombinovat jazyk C3 s assemblerem.

Poznámka: v předchozím odstavci je zmíněno, že podpora pro inline assembler v C3 je jednou z technologií, která C3 odlišuje od klasického C. Je nutné zmínit, že se jedná o porovnání se standardem jazyka C, protože většina (?) překladačů céčka assembler v nějaké formě podporuje.

2. Krátké ohlédnutí do minulosti

Assemblery za sebou mají velmi dlouhý vývoj, protože první nástroje, které se začaly tímto názvem označovat, vznikly již v padesátých letech minulého století, a to na mainframech vyráběných společností IBM i jejími konkurenty (což byly firmy UNIVAC, Burroughs, Honeywell, General Electric atd.). Před vznikem skutečných assemblerů byla situace poněkud složitá. První aplikace pro mainframy totiž byly většinou programovány přímo ve strojovém kódu, který bylo nutné přímo zadávat z takzvaného řídicího panelu (control panel) počítače nebo načítat z externích paměťových médií (tedy v době vládnutí těchto mainframů: z děrných štítků, magnetických pásek atd.).

Programátoři, co nesnášíte BS, ale máte rádi business! Y Soft je česká firma s globálním dopadem (100+ zemí, 1M+ uživatelů a >100% meziroční růst). R&D úplně bez manažerů (130 developerů). Otevíráme 30 pozic pro Cloud a AI: Praha/Brno/Ostrava/remote. Zodpovědnost ano, mikro-management ne. Pojď někam, kde můžeš věci změnit.

Y Soft logo

Ovšem zapisovat programy přímo ve strojovém kódu je pochopitelně zdlouhavé, vedoucí k častým chybám a pro větší aplikace z mnoha důvodů nepraktické, o čemž se ostatně mohli relativně nedávno přesvědčit například i studenti programující na československém mikropočítači PMI-80 (na druhou stranu se ovšem jednalo o vynikající učební pomůcku).

Z důvodu usnadnění a zrychlení práce vývojářů a pro snížení počtu chyb (oprava chyby, resp. další iterace vývoje, byla velmi drahá a zdlouhavá) tedy vznikly první utility, jejichž úkolem bylo transformovat programy zapsané s využitím symbolických jmen strojových instrukcí do (binárního) strojového kódu určeného pro konkrétní typ počítače a jeho procesoru.

ASSEMBLERObrázek 1: Kód v assembleru je možné, pochopitelně pokud to daný assembler umožňuje, psát i strukturovaně, používat subrutiny a funkce atd. Zde se konkrétně jedná o assembler pro slavné osmibitové mikroprocesory MOS 6502.Autor: tisnik, podle licence: Rights Managed

Těmto pomocným programům, jejichž možnosti se postupně vylepšovaly (například do nich přibyla podpora textových maker, řízení víceprůchodového překladu, vytváření výstupních sestav s překládanými symboly, později i skutečné linkování s knihovnami atd.), se začalo říkat assemblery a jazyku pro symbolický zápis programů pak jazyk symbolických instrukcí či alternativně jazyk symbolických adres – assembly language (někdy též zkráceně nazývaný assembler, takže toto slovo má vlastně dodnes oba dva významy). Jednalo se o svým způsobem převratnou myšlenku: sám počítač byl totiž použit pro tvorbu programů, čímž odpadla namáhavá práce s tužkou a papírem. Posléze se zjistilo, že i programování přímo v assembleru je většinou pracné a zdlouhavé, takže se na mainframech postupně začaly používat různé vyšší programovací jazyky, zejména FORTRAN a COBOL. Použití vyšších programovacích jazyků bylo umožněno relativně vysokým výpočetním výkonem mainframů i (opět relativně) velkou kapacitou operační paměti; naopak se díky vyšším programovacím jazykům mohly aplikace přenášet na různé typy počítačů, což je nesporná výhoda.

Poznámka: dnešní vibe coding vlastně na tento koncept dále navazuje.
ASSEMBLER

Obrázek 2: Assembler pro počítače Commodore C64.

Autor: tisnik, podle licence: Rights Managed

Oživení zájmu o programování v assembleru přinesl vznik minipočítačů (například známé řady PDP) a na konci sedmdesátých let minulého století pak zcela nového fenoménu, který nakonec přepsal celé dějiny výpočetní techniky – domácích osmibitových mikropočítačů. Na osmibitových domácích mikropočítačích se používaly dva typy assemblerů. Prvním typem byly assemblery interaktivní, které uživateli nabízely poměrně komfortní vývojové prostředí, v němž bylo možné zapisovat jednotlivé instrukce v symbolické podobě, spouštět programy, krokovat je, vypisovat obsahy pracovních registrů mikroprocesoru atd.

Velkou výhodou byla nezávislost těchto assemblerů na rychlém externím paměťovém médiu (například disketové jednotce), který mnoho uživatelů a programátorů ani nevlastnilo. Druhý typ assemblerů je používán dodnes – jedná se vlastně o běžné překladače, kterým se na vstupu předloží zdrojový kód (uložený na kazetě či disketě) a po překladu se výsledný nativní kód taktéž uloží na paměťové médium (odkud ho lze následně spustit). Tyto assemblery byly mnohdy vybaveny více či méně dokonalým systémem maker (odtud ostatně pochází i označení macroassembler).

ASSEMBLER

Obrázek 3: Atari Macro Assembler.

Autor: tisnik, podle licence: Rights Managed

Assemblery byly mezi programátory poměrně populární i na osobních počítačích Amiga a Atari ST, a to i díky tomu, že instrukční kód mikroprocesorů Motorola 68000 byl do značné míry ortogonální, obsahoval relativně velké množství registrů (univerzální datové registry D0 až D7 a adresové registry A0 až A7) a navíc bylo možné používat i takové adresovací režimy, které korespondovaly s konstrukcemi používanými ve vyšších programovacích jazycích (přístupy k prvkům polí, přístup k lokálním proměnným umístěných v zásobníkovém rámci, autoinkrementace adresy atd.).

3. Assemblery v Linuxu

V této kapitole budeme pod termínem „assembler“ chápat programový nástroj určený pro transformaci zdrojového kódu naprogramovaného v jazyku symbolických adres do strojového kódu. Pro Linux vzniklo hned několik takových nástrojů, přičemž některé nástroje jsou komerční a jiné patří mezi open source. Z nekomerčních nástrojů, které nás samozřejmě zajímají především, se jedná o známý GNU Assembler, dále pak o nástroj nazvaný Netwide assembler (NASM), nástroj Yasm Modular Assembler či až překvapivě výkonný vasm. NASM a Yasm jsou pro první krůčky v assembleru velmi dobře použitelné, neboť mají dobře zpracovaný mechanismus reakce na chyby, dají se v nich psát čitelné programy atd. Určitý problém nastává v případě, kdy je nutné vyvíjet aplikace určené pro jinou architekturu, než je i386 či x86_64, a to z toho důvodu, že ani Netwide assembler ani Yasm nedokážou pracovat s odlišnou instrukční sadou. Naproti tomu GNU Assembler tímto problémem ani zdaleka netrpí, ovšem zápis assembleru se pro každou architekturu odlišuje (což se například týká i zápisu poznámek atd.).

GNU Assembler (gas) je součástí skupiny nástrojů nazvaných GNU Binutils. Jedná se o nástroje určené pro vytváření a správu binárních souborů obsahujících takzvaný „objektový kód“, dále nástrojů určených pro práci s knihovnami strojových funkcí i pro profilování. Mezi GNU Binutils patří vedle GNU Assembleru i linker ld, profiler gprof, správce archivů strojových funkcí ar, nástroj pro odstranění symbolů z objektových a spustitelných souborů strip a několik pomocných utilit typu nm, objdump, size a strings. GNU Assembler je možné použít buď pro překlad uživatelem vytvořených zdrojových kódů nebo pro zpracování kódů vygenerovaných překladači vyšších programovacích jazyků (GCC atd.). Zajímavé je, že všechny moderní verze GNU Assembleru podporují jak původní AT&T syntaxi, tak i (podle autora článku čitelnější) syntaxi používanou společností Intel.

Netwide Assembler (NASM) vznikl v době, kdy začali na operační systém Linux přecházet programátoři znající operační systémy DOS a (16/32bit) Windows. Tito programátoři byli většinou dobře seznámeni s možnostmi assemblerů, které se na těchto platformách používaly nejčastěji – Turbo Assembleru (TASM) společnosti Borland i Microsoft Macro Assembleru (MASM) a tak jim možnosti GNU Assembleru (který má své kořeny na odlišných architekturách) příliš nevyhovovaly. Výsledkem snah o vytvoření nástroje podobnému TASMu či MASMu byl právě NASM, který podporuje stejný způsob zápisu operandů instrukcí a navíc ještě zjednodušuje zápis těch instrukcí, u nichž je jeden operand tvořen nepřímou adresou. NASM byl následován projektem Yasm (fork+přepis), ovšem základní vlastnosti a především pak vazba na platformu i386 a x86_64 zůstaly zachovány (to mj. znamená, že například na Raspberry Pi možnosti těchto dvou nástrojů plně nevyužijeme, což je určitě škoda; situace je o to zajímavější, že původní autor NASMu nyní pracuje pro společnost ARM).

Poznámka: v praktické části dnešního článku si ukážeme kombinaci NASMu s jazykem C3, i když stejně dobře by bylo možné použít i odlišný assembler.

4. Assemblery vestavěné do vyšších programovacích jazyků

Jazyk C3 podporuje takzvaný inline assembler, tj. kombinaci zápisu zdrojového kódu jak v jazyku C3, tak i v assembleru. Ve skutečnosti se nejedná o žádný nový ani přelomový koncept, protože s touto technologií můžeme pracovat již téměř čtyřicet let. V této kapitole si ukážeme, jak tento problém řeší různé (historické i současné) programovací jazyky.

Velmi důležitým dialektem BASICu, který byl velmi úspěšný především v západních zemích (ovšem nikoli v ČSSR, kde panovala, podobně jako v dalších státech RVHP, poněkud specifická situace), je BBC BASIC, jehož původním autorkou je Sophie Wilsonová a mezi další autory, kteří například provedli portaci tohoto programovacího jazyka na jiné platformy, patří i známý Richard Russell, který stále o tomto jazyku udržuje stránky dostupné na adrese http://www.bbcbasic.co.uk/index.html (z této stránky si můžete stáhnout i variantu BBC BASICu doplněnou o podporu knihovny SDL 2 a dostupnou i na Linux. K dispozici je ovšem i čistě terminálová varianta určená opět pro Linux a mnohé další varianty (Windows apod.).

Mezi zajímavou (a v tomto případě i poměrně vzácnou a v jiných dialektem BASICu málo viděnou) vlastnost programovacího jazyka BBC BASIC patří možnost kombinace programu zapsaného v BASICu s částmi zapisovanými v assembleru, tj. v nízkoúrovňovém jazyku symbolických instrukcí odpovídajícímu (binárním) strojovým instrukcím použitého mikroprocesoru. Tato pro mnohé programátory důležitá vlastnost byla do BBC BASICu přidána ještě v dobách, kdy byl tento jazyk vyvíjen pro osmibitové mikroprocesory MOS 6502, ovšem později, spolu s poměrně rychlým rozšiřováním BBC BASICu na další platformy, byla přidána i podpora pro symbolické instrukce mikroprocesoru Intel 8080, Zilog Z80 a dalších osmibitových a o několik let později i šestnáctibitových a třicetidvoubitových mikroprocesorů, zejména úspěšných mikroprocesorových řad Intel 80×86 a Motorola M68000 používaných v mnoha osobních počítačích (zapomenout ovšem nesmíme ani na platformu ARM!).

Ve fragmentu programu, který je vypsán pod tímto odstavcem je ukázán způsob současného použití BASICu a assembleru osmibitového mikroprocesoru Zilog Z80. Povšimněte si především použití hranatých závorek pro označení části programu napsaného v assembleru i toho, že v assembleru se poznámky i odkazy pro skoky zapisují odlišným způsobem než v BASICu (v assembleru se používají textová navěští – labely, zatímco v BASICu čísla řádků – v tomto ohledu je assembler vysokoúrovňovějším jazykem než samotný BASIC):

100 DIM code 15        :REM Reserve space for 16 bytes of code
110 bdos=5
120 FOR pass=0 TO 1    :REM Implement 2-pass assembly
130 P%=code            :REM Set program counter at start of each pass
140 [OPT pass*3        ;Enter assembler and select listing on pass 2
150 LD D,95:LD E,ASC"!"
160 .loop              ;A label
170 LD C,2             ;Source statements
180 PUSH DE:CALL bdos:POP DE
190 INC E:DEC D:JR NZ,loop
200 RET:]              :REM Exit assembler
210 NEXT pass
220 CALL code          :REM Execute assembly language routine
ASSEMBLER

Obrázek 4: Další program psaný v BBC BASICu, který obsahuje části psané v assembleru.

Autor: tisnik, podle licence: Rights Managed

Podpora pro kombinaci kódu psaného ve vyšším programovacím jazyce s assemblerem se objevila i ve slavném Turbo Pascalu. Ten podporoval speciální blok asm-end, přičemž vestavěný assembler dokázal pracovat s lokálními i globálními proměnnými atd. Dokonce bylo možné psát i celé funkce v assembleru. Krátká ukázka:

var
    I : Integer;
begin
    I:=3;
    asm
        movl I,%eax
    end;
end;
ASSEMBLER

Obrázek 5: Vývojové prostředí Turbo Pascalu.

Autor: tisnik, podle licence: Rights Managed

Příklad celé funkce psané v assembleru. Tato funkce přistupuje ke globální proměnné nazvané OLDMODE:

ASSEMBLER

Obrázek 6: Celá funkce napsaná v assembleru, která je však součástí zdrojových kódů Pascalu.

Autor: tisnik, podle licence: Rights Managed

5. Assembler a programovací jazyk C

Programovací jazyk C je poměrně často označován termínem „přenositelný assembler“. Je tomu tak z toho důvodu, že je možné relativně snadno odhadnout, jakým způsobem jsou jednotlivé jazykové konstrukce překládány do strojového kódu (což ovšem v moderních překladačích s pokročilými optimalizacemi už není tak jednoznačné). Nicméně jazyk C obsahuje jen takové konstrukce, které mají svůj jednoznačný „obraz“ ve strojovém kódu. To však ale neznamená, že C plně nahrazuje assembler – tak tomu není a ani nemůže být, protože některé konstrukce jsou nejlépe realizovány právě v assembleru (a postupy využívající intrinsic atd. situaci řeší jen částečně). Z tohoto důvodu má kombinace assembleru a C dosti dlouhou tradici.

Příkladem může být dnes již historický jazyk Turbo C nebo Borland C (C++). Ten obsahoval podporu pro blok asm, podobně jako výše zmíněný Turbo Pascal pocházející od stejné společnosti (Borland). Příkladem může být blok zapsaný v assembleru, který v DOSu přepne grafický režim přes službu BIOSu. Po přepnutí do grafického režimu s rozlišením 320×200 pixelů se celá obrazovka vyplní barvovým vzorkem:

#include <dos.h>
#include <stdio.h>
 
int main(void) {
    unsigned char far *ptr = (unsigned char*)MK_FP(0xa000, 0000);
    unsigned int i;
    printf("%p\n", ptr);
    getch();
 
    asm {
        mov ah, 00h
        mov al, 13h
        int 10h
    }
 
    for (i=0; i<(unsigned)(320*200); i++) {
        *ptr++ = i;
    }
    getch();
    return 0;
}

Některé překladače jazyka C namísto bloku asm podporují odlišné způsoby kombinace C s assemblerem. Příkladem je překladač CC65, ve kterém je možné zapisovat jednotlivé instrukce tak, jakoby se jednalo o volání (pseudo)funkce nazvané __asm__, které se v prvním parametru předává řetězec se jménem instrukce i s jejími parametry. Zajímavé ovšem je, že této pseudofunkci je možné předávat například obsahy proměnných atd.

unsigned char a;
unsigned char b;
unsigned char c;
 
void main(void)
{
    __asm__ ("lda %v", a);
    __asm__ ("clc");
    __asm__ ("adc %v", b);
    __asm__ ("sta %v", c);
}
Poznámka: nutné je ovšem dodat, že CC65 podporuje osmibitový mikroprocesor MOS6502 s velmi jednoduchými instrukcemi, které většinou obsahují pouze jeden explicitně zadaný operand (druhým operandem bývá akumulátor). Z tohoto důvodu je možné výše uvedený způsob použít, protože se například nesetkáme s adresováním typu segment atd.

Společně s rozvojem překladačů, které dokážou provádět i poměrně složité optimalizace, se (možná poněkud paradoxně) možnosti kombinace jazyka C s assemblerem zkomplikovala. Je tomu tak z toho důvodu, že překladač již musí mít informaci o tom, jaké registry jsou v assemblerovském bloku modifikovány, které hodnoty je možné zahodit, které datové struktury byly modifikováno atd. Proto například GNU C sice podporuje kombinaci céčka s assemblerem, ale samotný zápis je již složitější – programátor musí překladači různým způsobem napovídat. Na druhou stranu může překladač sám použít vhodné pracovní registry atd. Příklad zápisu, ve kterém je určeno, že proměnná src bude v assemblerovském bloku čtena, zatímco do proměnné dst se bude provádět zápis, vypadá takto:

int main(void) {
    int src = 1;
    int dst;
 
    asm ("mov %1, %0\n\t"
        "add $1, %0"
        : "=r" (dst)
        : "r" (src));
 
    printf("%d\n", dst);
}

Překlad funkce main do assembleru dopadne následovně. Zvýrazněny jsou instrukce, které odpovídají bloku asm:

  400466:       55                      push   rbp
  400467:       48 89 e5                mov    rbp,rsp
  40046a:       48 83 ec 10             sub    rsp,0x10
  40046e:       c7 45 fc 01 00 00 00    mov    DWORD PTR [rbp-0x4],0x1
  400475:       8b 45 fc                mov    eax,DWORD PTR [rbp-0x4]
  400478:       89 c0                   mov    eax,eax
  40047a:       83 c0 01                add    eax,0x1
  40047d:       89 45 f8                mov    DWORD PTR [rbp-0x8],eax
  400480:       8b 45 f8                mov    eax,DWORD PTR [rbp-0x8]
  400483:       89 c6                   mov    esi,eax
  400485:       bf 80 11 40 00          mov    edi,0x401180
  40048a:       b8 00 00 00 00          mov    eax,0x0
  40048f:       e8 dc fe ff ff          call   400370 <printf@plt>
  400494:       b8 00 00 00 00          mov    eax,0x0
  400499:       c9                      leave
  40049a:       c3                      ret

6. Assembler a programovací jazyk C3

Nyní se již konečně dostáváme k programovacímu jazyku C3, konkrétně k jeho podpoře spolupráce s assemblerem. Současná verze překladače jazyka C3 podporuje tři způsoby kooperace s assemblerem:

  1. Jednotlivé instrukce assembleru lze zapisovat do pseudofunkceasm, tedy podobně, jako tomu bylo v případě překladače CC65. Ovšem interně se zapisované instrukce přímo posílají do backend překladače (LLVM) bez toho, aby docházelo k jakémukoli zpracování na straně překladače jazyka C3. Nedochází tedy ani ke kontrole chyb, ani například k vyhodnocení jmen proměnných atd. Co je ale horší – i samotný zápis instrukcí a jejich operandů je poplatný LLVM a tudíž dosti nečitelný.
  2. Dále je možné použít blok asm (nikoli pseudofunkci), což je koncept, který jsme mohli vidět použitý v Turbo Pascalu i v Turbo/Borland C/C++. I přes některá omezení se jedná o nejlepší způsob, protože lze snadno pracovat s céčkovskými proměnnými, jejich adresami atd. I samotný způsob zápisu instrukcí je poměrně dobře čitelný, jak si ostatně ukážeme na demonstračních příkladech.
  3. Poslední způsob vlastně již známe – kód v assembleru je překládán odděleně od kódu zapisovaného v jazyku C3. Posléze se využije schopnosti C3 přímo volat nativní kód bez nutnosti použití technologií typu FFI. Předností je možnost využití jakéhokoli assembleru, oddělení těch částí kódu, které jsou závislé na architektuře, od obecného kódu atd. Nevýhodou pak složitý přístup k proměnným C3, ovšem tímto způsobem se v assembleru většinou definují celé funkce a nikoli menší bloky.

7. Instrukce assembleru posílané přímo do výstupu pro backend překladač

Podívejme se nejdříve na první způsob, který byl zmíněn v předchozí kapitole, tedy na zápis pseudofunkce asm, které se v řetězci předávají instrukce posílané přímo do LLVM (samotný překladač C3 už neprovádí žádné kontroly). Pokusme se například implementovat volání služby jádra pro ukončení procesu. V 32bitovém režimu x86 se zavolá funkce číslo 1, jejímž parametrem je návratový kód. Číslo funkce je předáno v registru EAX, návratový kód v registru EBX a samotné vyvolání služby jádra zajistí INT 80:

module assembly;
import std::io;
 
fn void main()
{
    io::printn("About to exit");
    asm("movl $$1, %eax");
    asm("movl $$42, %ebx");
    asm("int $$0x80");
    io::printn("I'm still alive...");
}

V 64bitovém režimu x86–64 má stejná služba jádra číslo 60 a jádro se zavolá instrukcí SYSCALL. Program tedy bude pochopitelně odlišný:

module assembly;
import std::io;
 
fn void main()
{
    io::printn("About to exit");
    asm("movq $$60, %rax");
    asm("movq $$42, %rdi");
    asm("syscall");
    io::printn("I'm still alive...");
}

Povšimněte si společných rysů obou programů. Používá se zde AT&T syntaxe, v níž je napřed zapisován zdrojový operand a posléze operand cílový. Dále se před jména registrů zapisuje znak procent. A nejsložitější je zápis konstant, který vyžaduje použití dvojitých znaků dolaru – to vše proto, že se náš kód přímo posílá do LLVM.

Oba programy si můžeme snadno otestovat:

$ c3c compile-run asm_string_1.c3 
 
Program linked to executable './assembly'.
Launching ./assembly
About to exit
Program completed with exit code 42.
 
$ echo $?
42

popř. druhý z programů:

$ c3c compile-run asm_string_2.c3
Program linked to executable './assembly'.
Launching ./assembly
About to exit
Program completed with exit code 42.
 
$ echo $?
42

8. Přístup k lokálním proměnným bez specifikace jejich jména

Pokud budeme chtít, aby se návratový kód procesu přečetl z lokální proměnné, je nutné postupovat dosti nízkoúrovňovým způsobem, protože k proměnným není možné v pseudofunkci asm přistupovat přes jejich jméno. Musíme vědět, jakým způsobem jsou lokální proměnné uloženy na zásobníkovém rámci a adresovat tyto proměnné přes bázový registr, což znamená nutnost změny kódu při každém přidání nebo ubrání lokální proměnné.

32bitová varianta programu může vypadat takto (povšimněte si adresy proměnné s offsetem –4):

module assembly;
import std::io;
 
fn void main()
{
    uint return_value = 42;
 
    io::printn("About to exit");
    asm("movl $$1, %eax");
    asm("movl -4(%rbp), %ebx");
    asm("int $$0x80");
    io::printn("I'm still alive...");
}

64bitová varianta může vypadat takto. Zde je ovšem typ proměnné odlišný a má tedy i jinou relativní adresu:

module assembly;
import std::io;
 
fn void main()
{
    ulong return_value = 42;
 
    io::printn("About to exit");
    asm("movq $$60, %rax");
    asm("movq -8(%rbp), %rdi");
    asm("syscall");
    io::printn("I'm still alive...");
}

9. Kontrola, jakým způsobem byl překlad proveden – přepínač –emit-asm

V případě, že se kombinuje zdrojový kód psaný v jazyce C3 s assemblerem a především ve chvíli, kdy se používá pseudofunkce asm, je vhodné se přesvědčit o tom, jak vlastně vypadá výsledek překladu do assembleru. LLVM totiž dokáže vygenerovat mezikód v assembleru, který je dokonce i poměrně dobře čitelný. Abychom tento kód získali, je nutné překladači jazyka C3 předat přepínač –emit-asm. Po překladu by měl kromě spustitelného souboru vzniknout i adresář asm s podadresářem linux-x64 (popř. jiným podle použitého operačního systému a architektury mikroprocesoru). V tomto adresáři budou všechny soubory v assembleru, které jsou součástí výsledné aplikace (tedy včetně knihovních funkcí):

$ ls -1 -sh
 
total 5.3M
 40K assembly.s
 24K libc.s
156K std.atomic.s
172K std_collections_list.std.os.backtrace.Backtrace.s
 16K std.core.ascii.s
172K std.core.builtin.s
240K std.core.dstring.s
544K std.core.mem.allocator.s
 24K std.core.mem.s
 24K std.core.string.conv.s
316K std.core.string.s
 24K std.core.types.s
 80K std.io.file.s
100K std.io.os.s
2.1M std.io.s
476K std.math.math_rt.s
 32K std.math.s
 88K std.os.backtrace.s
276K std.os.linux.s
256K std.os.posix.s
224K std.os.process.s

Nás bude primárně zajímat první soubor assembly.s (pojmenovaný podle balíčku), který mj. obsahuje i sekvenci instrukcí, které jsme zapsali přímo do asm:

        .loc    1 9 5 is_stmt 1                 # asm_string_3.c3:9:5
        #APP
        movq    $60, %rax
        #NO_APP
        .loc    1 10 5                          # asm_string_3.c3:10:5
        #APP
        movq    %rdi, -8(%rbp)
        #NO_APP
        .loc    1 11 5                          # asm_string_3.c3:11:5
        #APP
        syscall
        #NO_APP
        leaq    .L.str.2(%rip), %rax
        movq    %rax, -136(%rbp)
        movq    $18, -128(%rbp)
Poznámka: povšimněte si, že se ve výsledném kódu oddělují instrukce generované překladačem od instrukcí vytvářených pseudofunkcí asm. Oddělení je provedeno komentáři #APP a #NO_APP. Pseudoinstrukce .loc specifikují řádky v původním zdrojovém kódu.

10. Blok asm zabudovaný přímo do jazyka C3

Kromě pseudofunkce asm, která je poměrně špatně použitelná, podporuje programovací jazyk C3 i blok nazvaný též asm. Ten je již v praxi mnohem užitečnější, protože umožňuje v zápisu instrukcí používat jména lokálních a globálních proměnných, jejich adresy, parametry funkce atd. Tímto konceptem se tedy jazyk C3 přibližuje Turbo Pascalu a Borland C/C++, které nabízí podobné technologie.

Poznámka: ovšem stále zde existují omezení, například obtížné řešení návratových hodnot atd. K tomuto omezení se vrátíme v navazujících kapitolách.

V dalším textu se pokusíme v bloku asm realizovat volání služby jádra sloužícího k okamžitému ukončení procesu, pochopitelně se specifikací návratového kódu:

module assembly;
import std::io;
 
fn void main()
{
    io::printn("About to exit");
    asm
    {
        // kód pro ukončení procesu se specifikací
        // návratového kódu procesu
    }
    io::printn("I'm still alive...");
}

11. Realizace volání služby operačního systému přes blok asm

Pro volání 32bitové služby jádra, která slouží k ukončení procesu, musíme nastavit pracovní registr EAX na hodnotu 1, do registru EBX vložit návratový kód a následně zavolat INT 80h. Tato sekvence tří instrukcí se v bloku asm realizuje velmi snadno, pouze nesmíme zapomenout vložit před jména registrů znak dolaru:

module assembly;
import std::io;
 
fn void main()
{
    io::printn("About to exit");
    asm
    {
        movl $eax, 1;
        xorl $ebx, $ebx;
        int 0x80;
    }
    io::printn("I'm still alive...");
}

Blok asm je převeden překladačem jazyka C3 do následující podoby:

        #APP
        movl    $1, %eax
        xorl    %ebx, %ebx
        int     $128
        #NO_APP
Poznámka: povšimněte si, že se C3 postaral o korektní prefixy registrů, konstant atd. To je velký rozdíl oproti pseudofunkci asm.

Samozřejmě nám nic nebrání v tom, aby návratový kód nebyl roven nule (výsledek operace XOR), ale například hodnotě 42:

module assembly;
import std::io;
 
fn void main()
{
    io::printn("About to exit");
    asm
    {
        movl $eax, 1;
        xorl $ebx, 42;
        int 0x80;
    }
    io::printn("I'm still alive...");
}

Ještě se pro zajímavost podívejme na volání 64bitové služby jádra. Zde se liší jak použité registry (64bitové), tak i jejich jména a především se volá služba s jiným číslem:

module assembly;
import std::io;
 
fn void main()
{
    io::printn("About to exit");
    asm
    {
        movq $rax, 60;
        movq $rdi, 42;
        syscall;
    }
    io::printn("I'm still alive...");
}

Překlad do assembleru vypadá následovně:

        #APP
        movq    $60, %rax
        movq    $42, %rdi
        syscall
        #NO_APP

12. Přístup k lokálním proměnným z bloku asm s využitím jejich jména

Velmi užitečné je, že je možné v bloku asm používat názvy lokálních i globálních proměnných popř. parametry funkcí a metod. Můžeme si to otestovat na příkladu, ve kterém budeme jak číslo služby jádra, tak i požadovanou návratovou hodnotu procesu ukládat do dvojice lokálních proměnných nazvaných sys_exit a return_value (vhodného typu!). Tyto proměnné potom přímo použijeme v assembleru:

module assembly;
import std::io;
 
fn void main()
{
    long sys_exit = 60;
    long return_value = 42;
 
    io::printn("About to exit");
    asm
    {
        movq $rax, sys_exit;
        movq $rdi, return_value;
        syscall;
    }
    io::printn("I'm still alive...");
}

A jak bude vypadat překlad do assembleru? C3 za nás provede veškerou adresaci proměnných (ty jsou uloženy na zásobníkovém rámci), takže výsledek bude vypadat následovně:

        .loc    1 10 5 is_stmt 1                # asm_block_5.c3:10:5
        movq    -8(%rbp), %rdx
        movq    -16(%rbp), %rsi
        #APP
        movq    %rdx, %rax
        movq    %rsi, %rdi
        syscall
 
        #NO_APP
        leaq    .L.str.2(%rip), %rax
        movq    %rax, -152(%rbp)
        movq    $18, -144(%rbp)
Poznámka: tuto techniku již můžeme nazývat kvalitní kombinací C3 s assemblerem.

13. Jakým způsobem se předávají parametry do funkcí a jak se z nich vrací hodnoty?

Náš kód zapsaný v assembleru byl prozatím velmi jednoduchý, protože byl přímo zařazen do nějaké (delší) funkce. Ovšem často se setkáme s požadavkem, aby byla v assembleru naprogramována celá funkce, tj. včetně části určené pro čtení předaných parametrů i části, která se stará o návratové hodnoty. Nejdříve si ověříme, jakým způsobem se vlastně do funkcí (naprogramovaných přímo v C3) předávají parametry a jak se popř. konstruuje zásobníkový rámec a vytváří (vrací) návratové hodnoty.

Do programu zapsaného v C3 zařadíme funkci add a posléze prozkoumáme její kód v assembleru:

module assembly;
import std::io;
 
fn int add(int x, int y)
{
    return x+y;
}
 
fn void main()
{
    io::printfn("1+2=%d", add(1, 2));
}

Výsledek překladu do assembleru se zákazem optimalizací dopadne následovně:

        .file   "assembly"
        .text
        .globl  assembly.add                    # -- Begin function assembly.add
        .p2align        4
        .type   assembly.add,@function
assembly.add:                           # @assembly.add
.Lfunc_begin0:
        .file   1 "/tmp/ramdisk/c3c/build" "asm_block_6.c3"
        .loc    1 4 0                           # asm_block_6.c3:4:0
        .cfi_startproc
# %bb.0:
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset %rbp, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register %rbp
        movl    %edi, -4(%rbp)
        movl    %esi, -8(%rbp)
.Ltmp0:
        .loc    1 6 12 prologue_end             # asm_block_6.c3:6:12
        movl    -4(%rbp), %eax
        addl    -8(%rbp), %eax
        .loc    1 6 12 epilogue_begin is_stmt 0 # asm_block_6.c3:6:12
        popq    %rbp
        .cfi_def_cfa %rsp, 8
        retq
.Ltmp1:
.Lfunc_end0:
        .size   assembly.add, .Lfunc_end0-assembly.add
        .cfi_endproc

Můžeme zde vidět uložení parametrů (EDI a ESI) na zásobníkový rámec:

        movl    %edi, -4(%rbp)
        movl    %esi, -8(%rbp)

Samotný zásobníkový rámec je vytvořen popř. odstraněn těmito instrukcemi (což je standardní způsob známý snad již půl století):

        pushq   %rbp
        movq    %rsp, %rbp
        ...
        ...
        ...
        popq    %rbp
        retq

Celý kód funkce add je poměrně komplikovaný. Výhodnější bude provést překlad s povolením optimalizací:

        .file   "assembly"
        .text
        .globl  assembly.add                    # -- Begin function assembly.add
        .p2align        4
        .type   assembly.add,@function
assembly.add:                           # @assembly.add
        .cfi_startproc
# %bb.0:
                                        # kill: def $esi killed $esi def $rsi
                                        # kill: def $edi killed $edi def $rdi
        leal    (%rdi,%rsi), %eax
        retq
.Lfunc_end0:
        .size   assembly.add, .Lfunc_end0-assembly.add

Zde se žádný zásobníkový rámec netvoří a v těle funkce se pouze provede výpočet EAX=RDI+RSI. To tedy znamená, že již víme, jak se vrací návratová hodnota – právě přes pracovní registr EAX.

14. Implementace celé funkce ve vestavěném assembleru?

Otázkou nyní je, jestli a jak je možné ve vestavěném assembleru (tedy v bloku asm) implementovat celou plnohodnotnou funkci. V současné verzi jazyka C3 to pravděpodobně možné není, protože je nutné nějakým způsobem zajistit vracení hodnoty do volajícího kódu. Tj. sice můžeme v assembleru vytvořit prakticky celou funkci (včetně zpracování jejich parametrů), ovšem kromě závěrečného příkazu return. Výsledek by mohl vypadat takto:

module assembly;
import std::io;
 
fn int add(int x, int y)
{
    int z;
    asm {
        movl $eax, x;
        addl $eax, y;
        movl z, $eax;
    }
    return z;
}
 
fn void main()
{
    io::printfn("1+2=%d", add(1, 2));
}

Podívejme se na výsledek překladu, přičemž se omezíme pouze na funkci add:

        .file   "assembly"
        .text
        .globl  assembly.add                    # -- Begin function assembly.add
        .p2align        4
        .type   assembly.add,@function
assembly.add:                           # @assembly.add
        .cfi_startproc
# %bb.0:
        pushq   %rax
        .cfi_def_cfa_offset 16
        #APP
        movl    %edi, %eax
        addl    %esi, %eax
        movl    %eax, %ecx
 
        #NO_APP
        movl    %ecx, %eax
        popq    %rcx
        .cfi_def_cfa_offset 8
        retq
.Lfunc_end0:
        .size   assembly.add, .Lfunc_end0-assembly.add
        .cfi_endproc

Povšimněte si, že se s výsledkem zbytečně provádí dvě instrukce – jednu jsme zapsali sami, druhá je výsledkem překladu příkazu return:

        movl    %eax, %ecx
 
        #NO_APP
        movl    %ecx, %eax

15. Volání funkcí naprogramovaných v „externím“ assembleru

V mnoha případech se setkáme s požadavkem na zařazení již dříve vytvořených funkcí naprogramovaných v assembleru do programu, který je vytvořen v jazyku C3. Tento požadavek je možné relativně snadno splnit:

  1. Funkce naprogramovaná v assembleru je přeložena (assemblerem) do objektového kódu. Ten má na Linuxu koncovku .o, ve Windows pak .obj.
  2. Tento objektový kód je zařazen do sdílené knihovny (shared object, DLL). Můžeme použít například příkaz ar apod.
  3. Externí nativní funkce lze z jazyka C3 volat naprosto stejným způsobem, jako funkce definované přímo v jazyku C3.

Nevýhodou je, že funkce naprogramované v assembleru jsou prakticky zcela izolovány od zbytku aplikace, takže nemají přímý přístup k proměnným ani k jiným funkcím. Taktéž se musíme sami postarat o to, že kód funkcí v assemleru je nezávislý na umístění (PIC – Position-independent Code).

16. Krátké zopakování: volání externích funkcí naprogramovaných v jazyku C

Zopakujme si v krátkosti postup, který nám umožní 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;
}
Poznámka: nezapomeňte na modifikátor extern.

V dalším kroku provedeme překlad této funkce do objektového kódu s vložením tohoto kódu do sdílené knihovny nazvané libadder.so (samozřejmě lze použít libovolné jméno):

$ 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é. Nesmíme jen zapomenout na to, že hlavička volané funkce musí být v C3 taktéž označena jako extern:

module assembly;
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=.

V přeloženém programu vypadá volání nativní funkce add takto (opět ověřeno s využitím přepínače –emit-asm):

        movl    $1, %edi
        movl    $2, %esi
.Ltmp0:
        .loc    1 8 24 prologue_end             # call_add_1.c3:8:24
        callq   add@PLT

Pro zajímavost se můžeme podívat, jak vlastně vypadá tělo céčkovské funkce po překladu. K tomuto účelu lze využít různé nástroje (včetně debuggeru), ovšem nejjednodušší bude využití standardního nástroje objdump:

$ objdump -d adder.o

Výsledkem bude výpis disassemblovaného kódu, který bude vypsán s použitím AT&T syntaxe:

adder.o:     file format elf64-x86-64
 
 
Disassembly of section .text:
 
0000000000000000 <add>:
   0:   8d 04 37                lea    (%rdi,%rsi,1),%eax
   3:   c3                      ret

Čitelnější je však (alespoň pro autora článku) syntaxe firmy Intel:

$ objdump -d adder.o -M intel
 
adder.o:     file format elf64-x86-64
 
 
Disassembly of section .text:
 
0000000000000000 <add>
   0:   8d 04 37                lea    eax,[rdi+rsi*1]
   3:   c3                      ret
Poznámka: povšimněte si, že se v tomto případě provádí výpočet: EAX=RDI+RSI.

17. Zavolání funkce naprogramované v assembleru z jazyka C3

Nyní, když již víme, jakým způsobem je možné z programovacího jazyka C3 volat nativní funkci, se můžeme pokusit takovou funkci naprogramovat přímo v assembleru. Nebude to nic složitého, protože musíme pouze vědět, jak se předávají parametry do volané funkce a jak je vyřešeno zpětné předání návratové hodnoty volanému kódu. V ABI Linuxu se první dva (32bitové) celočíselné parametry předávají v registrech EDI a ESI (tedy nikoli přes zásobník) a návratová hodnota je uložena do pracovního registru EAX. To znamená, že můžeme provést snadný výpočet (není příliš optimalizovaný, ale je dobře čitelný):

     mov eax, edi
     add eax, esi
     ret

Pro implementaci této jednoduché funkce využijeme assembler NASM, který nabízí čitelnou syntaxi. Celý zdrojový kód může vypadat následovně. Povšimněte si především toho, že je funkce označena jako global. Na to se nesmí zapomenout, protože jinak by nebylo jméno funkce korektně exportováno:

BITS 64
 
global add
 
section .text
 
add:
     mov eax, edi
     add eax, esi
     ret

Nejprve provedeme překlad assemblerem do objektového souboru adder.o. Musíme přitom explicitně určit formát výsledného souboru přepínačem -f elf64:

$ nasm -f elf64 adder.asm

Podívejme se, jak vypadá obsah výsledného objektového souboru:

$ objdump -d adder.o

Výstup ve formátu AT&T:

adder.o:     file format elf64-x86-64
 
 
Disassembly of section .text:
 
0000000000000000 <add>:
   0:   89 f8                   mov    %edi,%eax
   2:   01 f0                   add    %esi,%eax
   4:   c3                      ret

Alternativně si samozřejmě můžeme vyžádat výstup ve formátu společnosti Intel:

$ objdump -d adder.o -M intel

Výsledek:

adder.o:     file format elf64-x86-64
 
 
Disassembly of section .text:
 
0000000000000000 <add>:
   0:   89 f8                   mov    eax,edi
   2:   01 f0                   add    eax,esi
   4:   c3                      ret

Z objektového souboru lze sdílenou knihovnu vytvořit různými způsoby, například přímo gcc:

$ gcc -shared -Wl,-soname,libadder.so -o libadder.so adder.o

Funkce add uložená do sdílené knihovny se zavolá naprosto stejným způsobem, jako tomu bylo u nativní funkce stejného jména naprogramované v jazyku C:

module assembly;
import std::io;
 
extern fn int add(int a, int b);
 
fn void main()
{
    io::printf("%d\n", add(1, 2));
}

18. Shrnutí

Programovací jazyk C3 nabízí vývojářům tři základní technologie umožňující kombinaci zdrojového kódu zapsaného přímo v C3 s kódem psaným v assembleru. Nejjednodušší a v mnoha případech plně dostačující je využití bloku asm, který umožňuje přímo v assembleru přistupovat ke globálním i lokálním proměnným či k jejich adresám, což je v praxi výhodné. Nevýhodou (alespoň prozatím) je nemožnost zapisovat tímto způsobem „čistě assemblerovské“ funkce. Ovšem díky tomu, že C3 plně podporuje céčkovské ABI, můžeme takové funkce naprogramovat přímo v assembleru (my jsme použili NASM, ale samozřejmě je možné využít YASM nebo GNU Assembler) a využít volání této funkce z C3. Nevýhodou je oddělený překlad a taktéž nutnost mít hlavičku funkce uloženou ve dvou zdrojových kódech. A poslední možnost představuje pseudofunkce asm, která je však dosti nízkoúrovňová a navíc samotný jazyk C3 nemá žádnou kontrolu nad předávanými/generovanými instrukcemi.

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/intro­duction/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/intro­duction/factorial_macro.c3
       
3 swap_macro.c3 makro realizující prohození dvou hodnot https://github.com/tisnik/c3-examples/blob/master/intro­duction/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/intro­duction/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/intro­duction/renderer_v1.c3
6 renderer_v2.c3 anotace parametrů funkcí typu ukazatel (pointer) https://github.com/tisnik/c3-examples/blob/master/intro­duction/renderer_v2.c3
7 renderer_v3.c3 statická kontrola, zda se nepředávají neinicializované ukazatele https://github.com/tisnik/c3-examples/blob/master/intro­duction/renderer_v3.c3
8 renderer_v4.c3 runtime kontrola, zda se nepředávají neinicializované ukazatele https://github.com/tisnik/c3-examples/blob/master/intro­duction/renderer_v4.c3
9 renderer_v5.c3 první (nekorektní) varianta funkce pro inicializaci barvové palety https://github.com/tisnik/c3-examples/blob/master/intro­duction/renderer_v5.c3
10 renderer_v6.c3 druhá (korektní) varianta funkce pro inicializaci barvové palety https://github.com/tisnik/c3-examples/blob/master/intro­duction/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/intro­duction/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/intro­duction/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:

# Příklad Stručný popis Adresa
37 01_vector_type.c3 definice vektoru, modifikace prvků vektoru, tisk obsahu celého vektoru https://github.com/tisnik/c3-examples/blob/master/c3-containers/01_vector_type.c3
38 02_vector_parameter.c3 předání vektoru do funkce hodnotou https://github.com/tisnik/c3-examples/blob/master/c3-containers/02_vector_parameter.c3
39 03_vector_pointer.c3 předání vektoru do funkce odkazem (přes ukazatel) https://github.com/tisnik/c3-examples/blob/master/c3-containers/03_vector_pointer.c3
       
40 04_array_type.c3 definice pole, modifikace prvků pole, tisk obsahu celého pole https://github.com/tisnik/c3-examples/blob/master/c3-containers/04_array_type.c3
41 05_array_parameter.c3 předání pole do funkce hodnotou https://github.com/tisnik/c3-examples/blob/master/c3-containers/05_array_parameter.c3
42 06_array_pointer.c3 předání pole do funkce odkazem (přes ukazatel) https://github.com/tisnik/c3-examples/blob/master/c3-containers/06_array_pointer.c3
       
43 07_slice_type.c3 vytvoření (konstrukce) řezu (slice) https://github.com/tisnik/c3-examples/blob/master/c3-containers/07_slice_type.c3
44 08_slice_parameter.c3 předání řezu do funkce https://github.com/tisnik/c3-examples/blob/master/c3-containers/08_slice_parameter.c3
45 09_slice_slicing.c3 konstrukce řezu z pole stejně pojmenovanou operací (řez od..do) https://github.com/tisnik/c3-examples/blob/master/c3-containers/09_slice_slicing.c3
46 10_slice_slicing.c3 konstrukce řezu z pole stejně pojmenovanou operací (záporné indexy) https://github.com/tisnik/c3-examples/blob/master/c3-containers/10_slice_slicing.c3
47 11_slice_slicing.c3 konstrukce řezu z pole stejně pojmenovanou operací (určení délky řezu) https://github.com/tisnik/c3-examples/blob/master/c3-containers/11_slice_slicing.c3
48 12_slice_of_slice.c3 konstrukce řezu z jiného řezu https://github.com/tisnik/c3-examples/blob/master/c3-containers/12_slice_of_slice.c3
       
49 13_list_type.c3 vytvoření (konstrukce) seznamu https://github.com/tisnik/c3-examples/blob/master/c3-containers/13_list_type.c3
50 14_list_parameter.c3 předání seznamu do funkce https://github.com/tisnik/c3-examples/blob/master/c3-containers/14_list_parameter.c3
       
51 15_dynamic_array.c3 vytvoření (konstrukce) dynamicky alokovaného pole https://github.com/tisnik/c3-examples/blob/master/c3-containers/15_dynamic_array.c3
       
52 16_string_type.c3 základní typ řetězce string https://github.com/tisnik/c3-examples/blob/master/c3-containers/16_string_type.c3
53 17_string_unicode.c3 Unicode znaky v řetězci typu string https://github.com/tisnik/c3-examples/blob/master/c3-containers/17_string_unicode.c3
54 18_zstring_type.c3 řetězce ukončené nulou (C-string, ASCIIZ) https://github.com/tisnik/c3-examples/blob/master/c3-containers/18_zstring_type.c3
55 19_zstring_unicode.c3 Unicode znaky v řetězci typu zstring https://github.com/tisnik/c3-examples/blob/master/c3-containers/19_zstring_unicode.c3
56 20_string_comparison.c3 porovnávání obsahu řetězců https://github.com/tisnik/c3-examples/blob/master/c3-containers/20_string_comparison.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:

# Příklad Stručný popis Adresa
82 01_regular_variable.c3 definice běžné proměnné typu int, přístup k hodnotě této proměnné https://github.com/tisnik/c3-examples/blob/master/c3-errors-handling/01_regular_variable.c3
83 02_optional_value.c3 definice proměnné typu Optional int, pokus o přímý přístup k hodnotě této proměnné (nekorektní varianta) https://github.com/tisnik/c3-examples/blob/master/c3-errors-handling/02_optional_value.c3
84 03_optional_value.c3 korektní čtení proměnné typu Optional int s detekcí chyby https://github.com/tisnik/c3-examples/blob/master/c3-errors-handling/03_optional_value.c3
85 04_optional_value.c3 korektní čtení proměnné typu Optional int s detekcí chyby https://github.com/tisnik/c3-examples/blob/master/c3-errors-handling/04_optional_value.c3
86 05_fault.c3 uživatelsky definovaný typ nesoucí informaci o chybě https://github.com/tisnik/c3-examples/blob/master/c3-errors-handling/05_fault.c3
87 06_fault.c3 uživatelsky definovaný typ nesoucí informaci o chybě https://github.com/tisnik/c3-examples/blob/master/c3-errors-handling/06_fault.c3
88 07_force_unwrap.c3 použití operátoru !! (force unwrap) https://github.com/tisnik/c3-examples/blob/master/c3-errors-handling/07_force_unwrap.c3
89 08_force_unwrap.c3 použití operátoru !! (force unwrap) https://github.com/tisnik/c3-examples/blob/master/c3-errors-handling/08_force_unwrap.c3
       
90 09_factorial.c3 běžný výpočet faktoriálu bez reakce na neplatný vstup https://github.com/tisnik/c3-examples/blob/master/c3-errors-handling/09_factorial.c3
91 10_factorial.c3 výpočet faktoriálu s reakcí na neplatný vstup – řešení bez kontroly návratové hodnoty https://github.com/tisnik/c3-examples/blob/master/c3-errors-handling/10_factorial.c3
92 11_factorial.c3 výpočet faktoriálu s reakcí na neplatný vstup – řešení s kontrolou návratové hodnoty https://github.com/tisnik/c3-examples/blob/master/c3-errors-handling/11_factorial.c3
       
93 12_defer_basic_usage.c3 konstrukce defer v programovacím jazyce C3 https://github.com/tisnik/c3-examples/blob/master/c3-errors-handling/12_defer_basic_usage.c3
94 13_more_defers.c3 pořadí provádění příkazů v blocích defer https://github.com/tisnik/c3-examples/blob/master/c3-errors-handling/13_more_defers.c3
95 14_defer_scope.c3 konstrukce defer definovaná ve vnitřních blocích v jazyce C3 https://github.com/tisnik/c3-examples/blob/master/c3-errors-handling/14_defer_scope.c3
96 15_defer_return.c3 konstrukce defer a return https://github.com/tisnik/c3-examples/blob/master/c3-errors-handling/15_defer_return.c3
       
97 16_init_finalize.c3 funkce označené jako @init a @finalizer https://github.com/tisnik/c3-examples/blob/master/c3-errors-handling/16_init_finalize.c3
       
98 defer.go konstrukce defer v programovacím jazyce Go https://github.com/tisnik/c3-examples/blob/master/c3-errors-handling/defer.go
99 defer_scope.go konstrukce defer definovaná ve vnitřních blocích v jazyce Go https://github.com/tisnik/c3-examples/blob/master/c3-errors-handling/defer_scope.go

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_ar­guments.c3
       
108 09_sum.c3 realizace funkce pro výpočet součtu všech předaných hodnot https://github.com/tisnik/c3-examples/blob/master/c3-functions/09_sum.c3
109 10_sum.c3 předání obsahu pole do funkce s proměnným počtem parametrů https://github.com/tisnik/c3-examples/blob/master/c3-functions/10_sum.c3
110 11_varargs.c3 pořadí předávání parametrů do funkce s proměnným počtem parametrů (nekorektní způsob použití) https://github.com/tisnik/c3-examples/blob/master/c3-functions/11_varargs.c3
111 12_varargs.c3 pořadí předávání parametrů do funkce s proměnným počtem parametrů (korektní způsob použití) https://github.com/tisnik/c3-examples/blob/master/c3-functions/12_varargs.c3
       
112 13_optional.c3 funkce vracející hodnotu typu Optional https://github.com/tisnik/c3-examples/blob/master/c3-functions/13_optional.c3
113 14_optional.c3 využití operátoru ?? https://github.com/tisnik/c3-examples/blob/master/c3-functions/14_optional.c3
       
114 15_contract.c3 kontrakty uvedené u funkcí https://github.com/tisnik/c3-examples/blob/master/c3-functions/15_contract.c3
115 16_contract.c3 kontrakty uvedené u funkcí https://github.com/tisnik/c3-examples/blob/master/c3-functions/16_contract.c3
       
116 17_c_declaration.c3 deklarace funkce bez parametrů „céčkovým způsobem“ (nekorektní zápis) https://github.com/tisnik/c3-examples/blob/master/c3-functions/17_c_declaration.c3
117 18_check_return_type.c3 kontrola návratové hodnoty překladačem jazyka C3 https://github.com/tisnik/c3-examples/blob/master/c3-functions/18_check_return_type.c3
118 19_check_return_value.c3 kontrola počtu návratových hodnot překladačem jazyka C3 https://github.com/tisnik/c3-examples/blob/master/c3-functions/19_check_return_value.c3
119 20_in_out_params.c3 předání ukazatelů do volané funkce https://github.com/tisnik/c3-examples/blob/master/c3-functions/20_in_out_params.c3
120 21_in_out_params.c3 označení ukazatelů kontrakty [in] a [out] https://github.com/tisnik/c3-examples/blob/master/c3-functions/21_in_out_params.c3
121 22_in_out_params.c3 označení ukazatelů kontrakty [in] a [out] https://github.com/tisnik/c3-examples/blob/master/c3-functions/22_in_out_params.c3
122 23_void_pointer.c3 předávání ukazatelů typu void * do volané funkce https://github.com/tisnik/c3-examples/blob/master/c3-functions/23_void_pointer.c3
123 24_contract.c3 kontrakt zapsaný před hlavičkou funkce https://github.com/tisnik/c3-examples/blob/master/c3-functions/24_contract.c4

Demonstrační příklady z předchozího článku o funkcích v C3:

# Příklad Stručný popis Adresa
124 25_function_type.c3 definice nového datového typu „funkce“ https://github.com/tisnik/c3-examples/blob/master/c3-functions/25_function_type.c3
       
125 26_short_function.c3 zkrácený způsob zápisu běžné funkce https://github.com/tisnik/c3-examples/blob/master/c3-functions/26_short_function.c3
126 27_short_default_argument.c3 zkrácený způsob zápisu funkce s výchozí hodnotou parametru https://github.com/tisnik/c3-examples/blob/master/c3-functions/27_short_default_argument.c3
127 28_short_default_argument.c3 zkrácený způsob zápisu funkce s výchozí hodnotou parametrů https://github.com/tisnik/c3-examples/blob/master/c3-functions/28_short_default_argument.c3
128 29_short_named_argument.c3 zkrácený způsob zápisu funkce, při jejím volání jsou explicitně uvedena jména parametrů https://github.com/tisnik/c3-examples/blob/master/c3-functions/29_short_named_argument.c3
129 30_short_first.c3 zkrácený způsob zápisu funkce akceptující proměnný počet parametrů https://github.com/tisnik/c3-examples/blob/master/c3-functions/30_short_first.c3
130 31_short_ternary.c3 zkrácený způsob zápisu funkce, která ve svém těle vyžaduje ternární operátor https://github.com/tisnik/c3-examples/blob/master/c3-functions/31_short_ternary.c3
       
131 32_anonymous.c3 příklad, v němž jsou definovány dvě logicky podobné funkce https://github.com/tisnik/c3-examples/blob/master/c3-functions/32_anonymous.c3
132 33_anonymous.c3 využití anonymních funkcí namísto dvojice logicky podobných funkcí https://github.com/tisnik/c3-examples/blob/master/c3-functions/33_anonymous.c3
133 34_anonymous.c3 definice a zavolání anonymní funkce provedené na jediném řádku zdrojového kódu https://github.com/tisnik/c3-examples/blob/master/c3-functions/34_anonymous.c3
134 35_anonymous.c3 pokus o definici uzávěru (closure) https://github.com/tisnik/c3-examples/blob/master/c3-functions/35_anonymous.c3
       
135 36_function_pointers.c3 předání ukazatele na funkce https://github.com/tisnik/c3-examples/blob/master/c3-functions/36_function_pointers.c3
136 37_function_pointers.c3 předání ukazatele na funkce (zkrácený zápis funkce) https://github.com/tisnik/c3-examples/blob/master/c3-functions/37_function_pointers.c3
137 38_return_function.c3 funkce vracející jinou funkci https://github.com/tisnik/c3-examples/blob/master/c3-functions/38_return_function.c3
       
138 39_call_c_function.c3 volání standardní céčkové funkce puts https://github.com/tisnik/c3-examples/blob/master/c3-functions/39_call_c_function.c3
139 40_call_c_function.c3 volání nativní céčkové funkce add https://github.com/tisnik/c3-examples/blob/master/c3-functions/40_call_c_function.c3
140 41_call_c_function.c3 volání nativní céčkové funkce multiply s korektními datovými typy https://github.com/tisnik/c3-examples/blob/master/c3-functions/41_call_c_function.c3
141 42_call_c_function.c3 volání nativní céčkové funkce akceptující pole (první způsob) https://github.com/tisnik/c3-examples/blob/master/c3-functions/42_call_c_function.c3
142 43_call_c_function.c3 volání nativní céčkové funkce akceptující pole (druhý způsob) https://github.com/tisnik/c3-examples/blob/master/c3-functions/43_call_c_function.c3
141 42_call_c_function.c3 volání nativní céčkové funkce akceptující pole (první způsob) https://github.com/tisnik/c3-examples/blob/master/c3-functions/42_call_c_function.c3
       
143 44_method.c3 zavolání metody (první způsob) https://github.com/tisnik/c3-examples/blob/master/c3-functions/44_method.c3
144 45_method.c3 zavolání metody (druhý způsob) https://github.com/tisnik/c3-examples/blob/master/c3-functions/45_method.c3

Příklady z předchozího článku s popisem makrosystému jazyka C3:

# Příklad Stručný popis Adresa
145 01_add_macro.c3 jednoduché makro, které může být expandováno v rámci výrazu https://github.com/tisnik/c3-examples/blob/master/c3-macros/01_add_macro.c3
146 02_add_macro.c3 makro je generické, lze použít s hodnotami různých typů https://github.com/tisnik/c3-examples/blob/master/c3-macros/02_add_macro.c3
       
147 03_macro_expansion1.c expanze makro provedená preprocesorem jazyka C (parametry nejsou uzávorkovány) https://github.com/tisnik/c3-examples/blob/master/c3-macros/03_macro_expansion1.c
148 03_macro_expansion2.c expanze makro provedená preprocesorem jazyka C (parametry jsou uzávorkovány) https://github.com/tisnik/c3-examples/blob/master/c3-macros/03_macro_expansion2.c
149 03_macro_expansion.c3 řešení v jazyku C3 bez nutnosti uzávorkování parametrů makra https://github.com/tisnik/c3-examples/blob/master/c3-macros/03_macro_expansion.c3
150 04_macro_expansion1.c expanze makro provedená preprocesorem jazyka C (tělo makra není v závorkách) https://github.com/tisnik/c3-examples/blob/master/c3-macros/04_macro_expansion1.c
151 04_macro_expansion2.c expanze makro provedená preprocesorem jazyka C (tělo makra je v závorkách) https://github.com/tisnik/c3-examples/blob/master/c3-macros/04_macro_expansion2.c
152 04_macro_expansion.c3 řešení v jazyku C3 bez nutnosti zápisu těla makra do závorek https://github.com/tisnik/c3-examples/blob/master/c3-macros/04_macro_expansion.c3
       
153 05_typed_macro.c3 makro s definicí typů parametrů https://github.com/tisnik/c3-examples/blob/master/c3-macros/05_typed_macro.c3
154 06_typed_macro.c3 makro s definicí typů parametrů: příklad jeho expanze https://github.com/tisnik/c3-examples/blob/master/c3-macros/06_typed_macro.c3
       
155 07_compile_time1.c3 expanze makra v čase překladu: nefunkční varianta s konstantami https://github.com/tisnik/c3-examples/blob/master/c3-macros/07_compile_time1.c3
156 08_compile_time2.c3 expanze makra v čase překladu: funkční varianta s konstantami https://github.com/tisnik/c3-examples/blob/master/c3-macros/08_compile_time2.c3
157 09_compile_time3.c3 expanze makra v čase překladu: nefunkční varianta s proměnnými https://github.com/tisnik/c3-examples/blob/master/c3-macros/09_compile_time3.c3
       
158 10_swap_macro1.c3 realizace makra pro prohození obsahu dvou proměnných: nefunkční varianta https://github.com/tisnik/c3-examples/blob/master/c3-macros/10_swap_macro1.c3
159 11_swap_macro2.c3 realizace makra pro prohození obsahu dvou proměnných: funkční varianta https://github.com/tisnik/c3-examples/blob/master/c3-macros/11_swap_macro2.c3
       
160 12_varargs.c3 makro s proměnným počtem argumentů https://github.com/tisnik/c3-examples/blob/master/c3-macros/12_varargs.c3

Demonstrační příklady uvedené v článku o nedefinovaném chování v jazyce C3:

Školení Kubernetes

# Příklad Stručný popis Adresa
161 01_div_by_zero.c3 nedefinované chování při dělení nulou https://github.com/tisnik/c3-examples/blob/master/c3-undefined-behaviour/01_div_by_zero.c3
162 02_mod_by_zero.c3 nedefinované chování při výpočtu zbytku po dělení nulou https://github.com/tisnik/c3-examples/blob/master/c3-undefined-behaviour/02_mod_by_zero.c3
163 03_min_int1.c3 změna znaménka nejmenší celočíselné hodnoty (daného typu) se znaménkem https://github.com/tisnik/c3-examples/blob/master/c3-undefined-behaviour/03_min_int1.c3
164 04_min_int2.c3 změna znaménka nejmenší celočíselné hodnoty operací součinu https://github.com/tisnik/c3-examples/blob/master/c3-undefined-behaviour/04_min_int2.c3
165 05_undefined_value.c3 nedefinované hodnoty lokálních konstant v různých režimech překladu https://github.com/tisnik/c3-examples/blob/master/c3-undefined-behaviour/05_undefined_value.c3
166 06_out_of_bounds1.c3 přístup k prvku pole s indexem, který přesahuje meze pole https://github.com/tisnik/c3-examples/blob/master/c3-undefined-behaviour/06_out_of_bounds1.c3
167 07_out_of_bounds2.c3 přístup k prvku pole s indexem, který přesahuje meze pole https://github.com/tisnik/c3-examples/blob/master/c3-undefined-behaviour/07_out_of_bounds2.c3
168 08_null_pointer.c3 přístup do paměti přes nulový ukazatel https://github.com/tisnik/c3-examples/blob/master/c3-undefined-behaviour/08_null_pointer.c3
169 09_contract.c3 nedodržení specifikovaného kontraktu: podmínka pro vstupní parametry https://github.com/tisnik/c3-examples/blob/master/c3-undefined-behaviour/09_contract.c3
170 10_contract.c3 nedodržení specifikovaného kontraktu: podmínka pro výslednou hodnotu https://github.com/tisnik/c3-examples/blob/master/c3-undefined-behaviour/10_contract.c3
171 11_out_of_bounds_slice.c3 překročení meze u řezů (slice) https://github.com/tisnik/c3-examples/blob/master/c3-undefined-behaviour/11_out_of_bounds_slice.c3
172 12_local_variable_pointer.c přístup k lokální proměnné mimo oblast její platnosti (příklad v C) https://github.com/tisnik/c3-examples/blob/master/c3-undefined-behaviour/12_local_variable_pointer.c
173 12_local_variable_pointer.c3 přístup k lokální proměnné mimo oblast její platnosti (příklad v C3) https://github.com/tisnik/c3-examples/blob/master/c3-undefined-behaviour/12_local_variable_pointer.c3
174 13_div_by_minus_one.c3 změna znaménka nejmenší celočíselné hodnoty operací podílu https://github.com/tisnik/c3-examples/blob/master/c3-undefined-behaviour/13_div_by_minus_one.c3
175 14_signed_overflow.c3 přetečení celočíselné hodnoty se znaménkem https://github.com/tisnik/c3-examples/blob/master/c3-undefined-behaviour/14_signed_overflow.c3
176 15_negative_shift.c3 bitový posun o zápornou hodnotu https://github.com/tisnik/c3-examples/blob/master/c3-undefined-behaviour/15_negative_shift.c3
177 16_large_shift.c3 bitový posun o hodnotu přesahující bitovou šířku operandu https://github.com/tisnik/c3-examples/blob/master/c3-undefined-behaviour/16_large_shift.c3

Demonstrační příklady uvedené v článku o přetěžování operátorů:

# Příklad Stručný popis Adresa
178 add_operator1.c3 metoda add definovaná pro dvojici vektorů https://github.com/tisnik/c3-examples/blob/master/c3-operators-overloading/add_operator1.c3
179 add_operator2.c3 odlišný způsob volání metody add pro provedení součtu dvou vektorů https://github.com/tisnik/c3-examples/blob/master/c3-operators-overloading/add_operator2.c3
180 add_operator3.c3 přetížení operátoru + pro součet dvou vektorů https://github.com/tisnik/c3-examples/blob/master/c3-operators-overloading/add_operator3.c3
181 add_operator4.c3 idiomatický způsob zápisu definice přetíženého operátoru + https://github.com/tisnik/c3-examples/blob/master/c3-operators-overloading/add_operator4.c3
182 add_operator5.c3 úprava předchozího příkladu: předání vektoru odkazem, nikoli hodnotou https://github.com/tisnik/c3-examples/blob/master/c3-operators-overloading/add_operator5.c3
183 add_operator6.c3 současná definice přičtení skalární hodnoty i součtu dvou vektorů https://github.com/tisnik/c3-examples/blob/master/c3-operators-overloading/add_operator6.c3
184 add_operator7.c3 otestování komutativity operace součtu https://github.com/tisnik/c3-examples/blob/master/c3-operators-overloading/add_operator7.c3
185 add_operator8.c3 přetížení operátoru součtu pro prohozené operandy https://github.com/tisnik/c3-examples/blob/master/c3-operators-overloading/add_operator8.c3
186 add_operator9.c3 symetrická operace součtu https://github.com/tisnik/c3-examples/blob/master/c3-operators-overloading/add_operator9.c3
       
187 dot_product1.c3 skalární součin volaný přetíženým operátorem * https://github.com/tisnik/c3-examples/blob/master/c3-operators-overloading/dot_product1.c3
188 dot_product2.c3 úprava předchozího příkladu: předání vektoru odkazem, nikoli hodnotou https://github.com/tisnik/c3-examples/blob/master/c3-operators-overloading/dot_product2.c3
189 dot_product3.c3 skalární součin vs. násobení vektoru skalární konstantou https://github.com/tisnik/c3-examples/blob/master/c3-operators-overloading/dot_product3.c3
       
190 equality_operator.c3 přetížení operátoru rovnosti https://github.com/tisnik/c3-examples/blob/master/c3-operators-overloading/equality_operator.c3
191 unary_minus.c3 přetížení operátoru pro unární – (otočení znaménka) https://github.com/tisnik/c3-examples/blob/master/c3-operators-overloading/unary_minus.c3
       
192 index_operator1.c3 přetížení operátoru indexování pro celočíselné indexy https://github.com/tisnik/c3-examples/blob/master/c3-operators-overloading/index_operator1.c3
193 index_operator2.c3 úprava předchozího příkladu: předání vektoru odkazem, nikoli hodnotou https://github.com/tisnik/c3-examples/blob/master/c3-operators-overloading/index_operator2.c3
194 index_operator3.c3 přetížení operátoru indexování pro klíče, nikoli celočíselné indexy https://github.com/tisnik/c3-examples/blob/master/c3-operators-overloading/index_operator3.c3
195 index_set_operator.c3 přetížení operátoru indexování pro zápis do objektu https://github.com/tisnik/c3-examples/blob/master/c3-operators-overloading/index_set_operator.c3
196 len_operator.c3 přetížení speciálního operátoru len https://github.com/tisnik/c3-examples/blob/master/c3-operators-overloading/len_operator.c3

Demonstrační příklady z dnešního článku o podpoře assembleru v jazyku C3:

# Příklad Stručný popis Adresa
197 asm_string1.c3 volání 32bitové funkce jádra operačního systému pro ukončení procesu https://github.com/tisnik/c3-examples/blob/master/c3-assembly/asm_string1.c3
198 asm_string2.c3 volání 64bitové funkce jádra operačního systému pro ukončení procesu https://github.com/tisnik/c3-examples/blob/master/c3-assembly/asm_string2.c3
199 asm_string3.c3 přístup k lokální 32bitové proměnné přes její adresu na zásobníkovém rámci https://github.com/tisnik/c3-examples/blob/master/c3-assembly/asm_string3.c3
200 asm_string4.c3 přístup k lokální 64bitové proměnné přes její adresu na zásobníkovém rámci https://github.com/tisnik/c3-examples/blob/master/c3-assembly/asm_string4.c3
       
201 asm_block1.c3 příprava pro volání kódu zapsaného v assembleru https://github.com/tisnik/c3-examples/blob/master/c3-assembly/asm_block1.c3
202 asm_block2.c3 volání 32bitové funkce jádra operačního systému pro ukončení procesu https://github.com/tisnik/c3-examples/blob/master/c3-assembly/asm_block2.c3
203 asm_block3.c3 podobné předchozímu příkladu, ovšem používá se jedna odlišná instrukce https://github.com/tisnik/c3-examples/blob/master/c3-assembly/asm_block3.c3
204 asm_block4.c3 volání 64bitové funkce jádra operačního systému pro ukončení procesu https://github.com/tisnik/c3-examples/blob/master/c3-assembly/asm_block4.c3
205 asm_block5.c3 přístup k lokálním 64bitovým proměnným přes její adresu na zásobníkovém rámci https://github.com/tisnik/c3-examples/blob/master/c3-assembly/asm_block5.c3
       
206 asm_add1.c3 realizace funkce pro součet dvou hodnot v jazyku C3 https://github.com/tisnik/c3-examples/blob/master/c3-assembly/asm_add1.c3
207 asm_add2.c3 realizace funkce pro součet dvou hodnot v kombinaci assembler+C3 https://github.com/tisnik/c3-examples/blob/master/c3-assembly/asm_add2.c3
       
208 adder.c funkce pro součet dvou celých čísel naprogramovaná v jazyku C https://github.com/tisnik/c3-examples/blob/master/c3-assembly/adder.c
209 adder.asm funkce pro součet dvou celých čísel naprogramovaná v assembleru https://github.com/tisnik/c3-examples/blob/master/c3-assembly/adder.asm
210 call_add.c3 volání nativní funkce (může být naprogramovaná jak v C, tak i v assembleru) https://github.com/tisnik/c3-examples/blob/master/c3-assembly/call_add.c3

20. Odkazy na Internetu

  1. Programovací jazyk C3: evoluce, nikoli revoluce
    https://www.root.cz/clanky/pro­gramovaci-jazyk-c3-evoluce-nikoli-revoluce/
  2. Programovací jazyk C3: datové typy pro moderní architektury
    https://www.root.cz/clanky/pro­gramovaci-jazyk-c3-datove-typy-pro-moderni-architektury/
  3. Programovací jazyk C3: složené datové typy a kontejnery
    https://www.root.cz/clanky/pro­gramovaci-jazyk-c3-slozene-datove-typy-a-kontejnery/
  4. The C3 Programming Language
    https://c3-lang.org/
  5. C3 For C Programmers
    https://c3-lang.org/language-overview/primer/
  6. 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/Pro­grammingLanguages/comments/o­ohij6/c3_is_a_clike_langu­age_trying_to_be_an/
  7. Tiobe index
    https://www.tiobe.com/tiobe-index/
  8. PYPL PopularitY of Programming Language
    https://pypl.github.io/PYPL.html
  9. C3 Tutorial
    https://learn-c3.org/
  10. History of programming languages
    https://devskiller.com/history-of-programming-languages/
  11. History of programming languages (Wikipedia)
    https://en.wikipedia.org/wi­ki/History_of_programming_lan­guages
  12. D language
    https://dlang.org/
  13. Zig programming language
    https://ziglang.org/
  14. V language
    https://vlang.io/
  15. D programming language
    https://en.wikipedia.org/wi­ki/D_(programming_language)
  16. Zig programming language (Wikipedia)
    https://en.wikipedia.org/wi­ki/Zig_(programming_langu­age)
  17. V programming language (Wikipedia)
    https://en.wikipedia.org/wi­ki/V_(programming_language)
  18. Syntax highlighting for C3's programming language
    https://github.com/Airbus5717/c3.vim
  19. Go factorial
    https://gist.github.com/e­simov/9622710
  20. Generational list of programming languages
    https://en.wikipedia.org/wi­ki/Generational_list_of_pro­gramming_languages
  21. The Language Tree: Almost Every Programming Language Ever Made
    https://github.com/Phileo­sopher/langmap
  22. List of C-family programming languages
    https://en.wikipedia.org/wi­ki/List_of_C-family_programming_languages
  23. Compatibility of C and C++
    https://en.wikipedia.org/wi­ki/Compatibility_of_C_and_C%2B%2B
  24. C++23: compatibility with C
    https://www.sandordargo.com/blog/2023/08/23/cpp­23-c-compatibility
  25. Can C++ Run C Code? Understanding Language Compatibility
    https://www.codewithc.com/can-c-run-c-code-understanding-language-compatibility/
  26. C3: Comparisons With Other Languages
    https://c3-lang.org/faq/compare-languages/
  27. C3 Programming Language Gains Traction as Modern C Alternative
    https://biggo.com/news/202504040125_C3_Pro­gramming_Language_Alterna­tive_to_C
  28. The case against a C alternative
    https://c3.handmade.networ­k/blog/p/8486-the_case_against_a_c_alternative
  29. C (programming language) Alternatives
    https://alternativeto.net/software/c-programming-language-/
  30. Seriál Programovací jazyk Go
    https://www.root.cz/seria­ly/programovaci-jazyk-go/
  31. Is C3 the Underdog That Will Overtake Zig and Odin?
    https://bitshifters.cc/2025/05/22/c3-c-tradition.html
  32. „Hello, World!“ program
    https://en.wikipedia.org/wi­ki/%22Hello%2C_World!%22_pro­gram
  33. The C Programming Language
    https://en.wikipedia.org/wi­ki/The_C_Programming_Langu­age
  34. Kontejner (abstraktní datový typ)
    https://cs.wikipedia.org/wi­ki/Kontejner_(abstraktn%C3%AD_da­tov%C3%BD_typ)
  35. Are arrays not considered containers because they are not based off of a class?
    https://stackoverflow.com/qu­estions/37710975/are-arrays-not-considered-containers-because-they-are-not-based-off-of-a-class
  36. Array declaration (C, C++)
    https://en.cppreference.com/w/cpp/lan­guage/array.html
  37. Understanding the Apple ‘goto fail;’ vulnerability
    https://www.blackduck.com/blog/un­derstanding-apple-goto-fail-vulnerability-2.html
  38. Branch (computer science)
    https://en.wikipedia.org/wi­ki/Branch_(computer_scien­ce)
  39. Conditional (computer programming)
    https://en.wikipedia.org/wi­ki/Conditional_(computer_pro­gramming)
  40. Dangling else
    https://en.wikipedia.org/wi­ki/Dangling_else
  41. Switch statement
    https://en.wikipedia.org/wi­ki/Switch_statement
  42. Compiler correctness
    https://en.wikipedia.org/wi­ki/Compiler_correctness
  43. Anonymous function
    https://en.wikipedia.org/wi­ki/Anonymous_function
  44. Closure (computer programming)
    https://en.wikipedia.org/wi­ki/Closure_(computer_program­ming)
  45. How to implement closures in C
    https://hokstad.com/how-to-implement-closures
  46. An Overview of Macros in Rust
    http://words.steveklabnik.com/an-overview-of-macros-in-rust
  47. A Practical Intro to Macros in Rust 1.0
    https://danielkeep.github.io/practical-intro-to-macros.html
  48. The Rust Programming Language: macros
    https://doc.rust-lang.org/beta/book/macros.html
  49. Rust by example: 15 macro_rules!
    http://rustbyexample.com/macros.html
  50. Undefined behavior
    https://en.wikipedia.org/wi­ki/Undefined_behavior
  51. Undefined Behavior in C and C++
    https://www.geeksforgeeks­.org/cpp/undefined-behavior-c-cpp/
  52. The Rust reference: Behavior considered undefined
    https://doc.rust-lang.org/reference/behavior-considered-undefined.html#behavior-considered-undefined
  53. Why would a language have a concept of undefined behavior instead of raising an error?
    https://langdev.stackexchan­ge.com/questions/481/why-would-a-language-have-a-concept-of-undefined-behavior-instead-of-raising-an
  54. Undefined behavior
    https://riptutorial.com/c/to­pic/364/undefined-behavior
  55. C3: Undefined Behaviour
    https://c3-lang.org/language-rules/undefined-behaviour/
  56. Undefined behavior in C and C++
    https://russellw.github.io/undefined-behavior
  57. C3 goes game and maths friendly with operator overloading
    https://c3.handmade.networ­k/blog/p/9019-c3_goes_game_and_maths_fri­endly_with_operator_overlo­ading
  58. Are operator overloadings in C++ more trouble than they're worth?
    https://stackoverflow.com/qu­estions/707081/are-operator-overloadings-in-c-more-trouble-than-theyre-worth
  59. Why is operator overloading sometimes considered a bad practice?
    https://www.reddit.com/r/Pro­grammingLanguages/comments/19cl30z/why_is_o­perator_overloading_someti­mes_considered/
  60. I don't understand the arguments against operator overloading
    https://softwareengineerin­g.stackexchange.com/questi­ons/25154/i-dont-understand-the-arguments-against-operator-overloading
  61. BBC BASIC
    http://www.bbcbasic.co.uk/bbcba­sic.html
  62. BBC BASIC
    http://mdfs.net/Software/BBCBasic/
  63. BBC BASIC (Z80) for the ZX Spectrum
    http://mdfs.net/Software/BBCBa­sic/Spectrum/
  64. BBC BASIC (Wikipedia CZ)
    http://en.wikipedia.org/wi­ki/BBC_BASIC
  65. History of Pascal
    https://www.taoyue.com/tu­torials/pascal/history.html
  66. Antique Software: Turbo Pascal version 5.5
    http://edn.embarcadero.com/ar­ticle/20803
  67. Turbo Pascal Object-oriented programming guide
    http://edn.embarcadero.com/ar­ticle/images/20803/TP55_O­OP_Guide.pdf
  68. FreePascal
    http://www.freepascal.org/
  69. FreePascal (dokumentace dostupná online)
    https://www.freepascal.org/docs.html
  70. Turbo Pascal (With DOSBox)
    https://tpwdb.weebly.com/
  71. Modern Pascal
    http://modernpascal.com/
  72. Free Pascal: Using assembler in the sources
    https://www.freepascal.org/docs-html/prog/progse9.html#x143–1440003.1
  73. Is inline asm part of the ANSI C standard?
    https://stackoverflow.com/qu­estions/13870489/is-inline-asm-part-of-the-ansi-c-standard
  74. Position-independent code
    https://en.wikipedia.org/wiki/Position-independent_code
Neutrální ikona do widgetu na odběr článků ze seriálů

Zajímá vás toto téma? Chcete se o něm dozvědět víc?

Objednejte si upozornění na nově vydané články do vašeho mailu. Žádný článek vám tak neuteče.


Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.