- většina příkazů začíná
#
- dělá textové náhrady nad zdrojovým kódem (neví nic o syntaxi C -> lze použít pro jiné jazyky)
- příkazy preprocesoru jsou nahrazeny
#define JMÉNO_MAKRA hodnota_makra (v kódu se nahradí jméno makra za hodnotu)
- caps case je pouze konvence, ale doporučeno používat
- Makro je platné od řádku jeho uvedení
- pozor, makro může být platná i v dalších souborech (
#include)
- Platnost makra lze zrušit pomocí
undef jméno_makra
- Makro lze definovat v kódu nebo jako přepínač překladu
#define DEBUG
gcc -Djméno_makra => gcc -DDEBUG
gcc -Djméno_makra=hodnota_makra
- Makro může být předefinováno (spíš často chyba)
- Varování od překladače - potřeba nejprve oddefinovat
#ifdef DEBUG printf("Just testing")
- části progamů závislé na platformě
#if, #ifdef, ifndef, #else, #elif, #endif
- Makra můžeme použít pro náhradu funkcí - tzv. function-style macro
#define ADD(a, b) (a + b)
int main(void) {
int z = ADD(5, 7) * 3;
return 0;
}
- Makra s parametry typicky zavedeny z důvodu rychlosti
- pokud je standardní funkce, musí připravit zásobník…
- např. jednoduché sečtení dvou čísel může znatelně zpomalovat
- Překladač může vložit kód funkce namísto volání (tzv. inlining)
- Pomocí klíčového slova
inline signalizujeme funkci vhodnou pro inlining
- Makro nemá žádný typ
- pouze textová náhrada během preprocessingu
- velmi náchylné na textové překlepy
- problémy s rozvojem
- viz předchozí příklad
- dávat závorky okolo maker
- většinou špatné pro čitelnost kódu
- Kód s
goto lze vždy přepsat i na kód bez něj
- C nepodporuje výjimky –> goto na ošetření chyb
- Procesor (registry CPU)
- vykonání instrukce procesoru potřebujeme argumenty v registrech
- RAM (Zásobník)
- registr
esp ukazuje na aktuální pozici zásobníku
- lokální proměnné
- RAM (halda)
- dynamicky alokovaná paměť
- Ostatní paměť (HDD, …)
- Defaultní paměťová třída pro lokální proměnné
- automaticky vznik na zásobníku
- automatické odstranění při konci bloku
- V kódu se explicitně neuvádí
- Proměnná deklarovaná se
static zachová svou hodnotu i po konci bloku s deklarací
- Statické proměnné jsou inicializovány v době překladu
- trvalé místo pro proměnnou stejně jako pro globální proměnné
- při novém “vzniku” proměnné obsahuje poslední předešlou hodnotu
- Zachování hodnoty proměnné je jen v rámci jednoho spuštění programu
- Proměnná se
static je lokální v rámci souboru
- Proměnná může být měněna i mimo náš kód
- rutinou přerušení, sdílená paměť, …
- Pouze z analýzy zdrojového kódy nelze určit místa změny proměnné
- Vynutí nahrání hodnoty proměnné ze zásobníku do registru CPU před každou operací
- nelze provést optimalizace předpokládající přítomnost hodnoty proměnné v registru z předchozí operace
- pokud by došlo ke změně mimo náš kód, hodnota by nebyla aktuální
- Doporučení pro překladač, aby byla proměnná uložena přím v registru CPU
- před vykonáním instrukce musí být hodnoty do registru přeneseny (instrukce mov atp.)
- pokud je ale již v registru přítomná => zrychlení
- CPU má ale jen omezený počet registrů
register je jen doporučení, překladač může ignorovat
- Některé proměnné mohou být umístěny v registru i bez specifikace
register
- Paměťová třída pouze pro ukazatel
- slibujeme překladači, že na danou paměť ukazuje jen tento a žádný jiný používaný ukazatel
- pokud bude paměť měněna, tak pouze přes tento ukazatel
- Překladač může generovat optimalizovanější kód
- např. nemusí nahrávat opakovaně hodnotu do registru, pokud je zřejmé, že nebyla změněna
- [https://en.wikipedia.org/wiki/Restrict)(https://en.wikipedia.org/wiki/Restrict)
- Pokud porušíme, může dojít k nedefinovanému chováni
- zvažme, zda rychlostní optimalizace vyváží riziko zanesení chyby
- Proměnná nebo funkce je definovaná jinde, typicky v jiném zdrojovém souboru
- Defaultní volba pro funkční prototyp
- po ložení hlavičky s prototypem může překladač pokračovat, aniž by znal implementaci funkce
- Až během linkování se hledá implementace funkce resp. umístění proměnné
- Pro proměnné se používá v případě globální proměnné dostupné z několika zdrojových kódů
- Kontrola platnosti některých podmínek, které předpokládáme
- Některé funkce má smysl používat s různými počty a typy argumentů
- Argumenty na konci seznamu lze nahradit výpustkou
...
int printf(const char * format, ...);
- první argument je formátovací řetězec, dále
0 až N argumentů
- Výslovně uvedené argumenty jsou použity normálně
- Argumenty předané na pozici výpustky jsou přístupné pomocí dodatečných maker
- hlavičkový soubor
stdarg.h
va_start, va_arg a va_end
- Definujeme ve funkci proměnnou typu
va_list
- Naplníme proměnnou argumenty v proměnné části
va_start(argumets, number_of_arguments);
- Opakovaně získáváme jednotlivé argumenty
va_arg(arguments, type_of_argument);
- Uknčíme práci s argumenty
- Seznam argumentů lze zpracovat jen částečně a předat další funkci (která může zpracovat zbytek)
- Jazyk C neumožňuje zjisti počet argumentů při volání funkce
- lze přímo předat prvním argumentem:
- lze odvodit z prvního argumentu:
int printf(const char * format, ...);
format="%s%c%s%d" => 4 argumenty