7.0 Makra
7.0.1 Makra bez parametrů
Makra je specialita Céčka. Usnadní hodně práce při psaní, nejlepší bude když
je vysvětlím pomocí příkladů. Makra se v C definují jako příkaz preprocesouru
(se znakem # vězení na začátku) :
#define JMENOMAKRA hodnotamakra
Pozor ! Mezi JMENOMAKRA a hodnotamakra musí být mezera !
Potom, pokud se kdekoli (kromě jedné výjimky, popíšu dále) v programu
objeví JMENOMAKRA, kompilátor ji zamění za "hodnotamakra". Makra
se většinou píší velkými písmeny, ale pokud je malými, nebo oboje, není to chyba.
A teď k té výjimce... pokud je makro ve stringu, nebo komentáři, neprojeví se :
#define JMENO Luki
printf("Jmenuji se JMENO");
|
Vypíše :
Jmenuji se JMENO
a ne očekávané :
Jmenuji se Luki
A nyní praktický příklad :
#define PI 3.14159265
double uhel, radiany;
radiany = uhel * PI / 180.0;
|
A teď ještě malá pomůcka :
#define MAKRO toto je makro, které se nevejde \
na více řádků ...
|
Makro taky může něco dělat :
#define DO_IT { printf("Cekam !\n"); getchar(); }
|
Tohle makro vytiskne "Cekam !" a čeká na stisk klávesy.
Další použití makra je v tzv. podmíněném překladu :
7.0.1.1 Podmíněný překlad
Podmíněný překlad se používá třeba když máte dvě verze části programu, každou
pro jinou platformu. Nebo když máte část použitou pro ladění a pro běh programu.
Dělá se to pomocí maker :
#define LINUX 1
#define WINDOWS 0
#define WIN_NT 0
#if LINUX
// ... funkce, specificke pro linux
#elif WINDOWS && WIN_NT
// ... funkce, specificke pro windows NT
#elif WINDOWS
// ... funkce, specificke pro windows
#else
// ... funkce pro dos
#endif
|
Tohle dokáže rozhodnout, která část programu se bude překládat podle toho,
jakou hodnotu mají makra (1 = pravda, 0 = nepravda). Jde to i jinak :
#define LINUX
// #define WINDOWS
// #define WIN_NT
#if defined(LINUX)
// ... funkce, specificke pro linux
#elif defined(WINDOWS) && defined(WIN_NT)
// ... funkce, specificke pro windows NT
#elif defined(WINDOWS)
// ... funkce, specificke pro windows
#else
// ... funkce pro dos
#endif
|
Tady stačí, aby makro bylo definováno, nemusí mít žádnou hodnotu.
No a pokud nepotřebujete nic takového složitého, je tu ještě jedna možnost :
#define DEBUG
//#define RELEASE
#ifdef DEBUG
// ... funkce pro ladění (třeba výpisy stavu programu ..)
#endif
#ifndef RELEASE
// ... jde to i naopak
#endif
|
Tohle je příklad pro příkazy #ifdef, #ifndef. To se používá jěště
u funkcí. ale o tom až u nich.
7.0.1 Makra s parametry
Definují se skoro stejně :
#define MAX(x,y) ((x) > (y))? (x) : (y)
// Toto makro vybere maximální číslo ze dvou
// Volá se a = MAX(b, c); kde a, b a c jsou nějaké proměnné
|
Pozor ! Mezi jménem makra (MAX) a parametry (x,y) nesmí
být mezera, mezi parametry a hodnotou makra musí být mezera.
Možná se divíte, proč mám to x a y v závorkách :
Pokud jste četli předchozí část kapitoly o makrech, víte jak se makro
rozvíjí a proto kdyby tam nebyly, stalo by se asi tohle ...
#define MAX(x,y) (x > y)? x : y
// zde je záměrně CHYBA
|
Volání :
int a, b, c;
a = MAX(b + 1, c - 2);
|
A chybný rozvoj :
a = (b + 1 > c - 2)? b + 1 : c - 2 ;
|
Podmínka se nevyhodnotí jak asi čekáte, ale (1 > c) + b - 2. To je správná
(syntakticky)podmínka a pokud vyjde nenulová, program si myslí že platí. A se závorkami :
#define MAX(x,y) ((x) > (y)) ? (x) : (y)
a = ((b + 1) > (c - 2)) & (b + 1) : (c - 2);
|
7.1 Paměťové modifikátory
7.1.0 Modifikátor auto
Pokud deklarujete proměnnou bez modifikátorů,
je deklarovaná právě, jako auto.
int i;
je to samé jako :
auto int i;
7.1.1 Modifikátor static
Pokud je proměnná deklarovaná jako :
static int i;
Není přístupná z ostatních modulů, ale pokud je globální, zůstává globální i nadále.
Pokud je ale lokální, zůstává v ní její hodnota. To si ale vysvětlíme až u funkcí.
Výhoda tohoto modifikátoru je zrychlení.
7.1.2 Modifikátor register
Pokud deklarujete proměnnou jako :
register int i;
Může být tato proměnná uložená v registru procesoru a proto je
mnohokrát (až brutálně)rychlejší. Jen si musíte pamatovat že registrů
je proklatě málo a tak ne všechny proměnné můžou být v registru.
(většinou to vyjde tak na dvě)
7.1.3 Modifikátor volatile
Pokud není z nějakého důvodu vhodné, aby byla
proměnná uložená v registru, deklarujeme ji :
volatile int i;
... a ona proměnná bude pouze v paměti RAM. To se používá ve vícevláknovém
programování pro různé stavové proměnné nebo při spolupráci více paralelně
běžících programů. Ale o tom až někdy ...
7.2 Příkaz #include
Tímto příkazem vkládáte hlavičkové soubory, pokud je syntaxe :
#include <stdio.h>
Vložíte systémový soubor stdio.h, ale pokud to bude :
#include "main.h"
Vložíte soubor main.h, který kompilátor očekává v adresáři
projektu. A teď k čemu je to dobré ...
Pokud už jste zkoušeli napsat v C-čku něco většího, kde se
funkce volají mezi sebou, asi jste narazili nachybu, že
některé funkce nejdou najít ... Funkce má narozdíl od pro -
měnné deklaraci - to je řádek, kde je funkce vypsána
ten je povinný. a taky definici, čili funkční prototyp
a to je řádek, podle kterého kompilátor pozná, že funkce
se někde v programu vyskytuje ... Tvoří se asi takhle :
funkce :
int Nejake_Volani(int parametr){
return parametr + 1;
}
|
má funkční prototyp :
int Nejake_Volani(int parametr); // Ten středník musí být
|
Tohle sice patří k preprocesoru, ale jak to využít si povíme až u funkcí
za dvě kapitoly.
Cvičení :
1) - Napište program, který pomocí makra zjistí pokud je zadané číslo liché.
2) - Napište makro, které vybere prostřední ze tří zadaných čísel (velikostně)
3) - Napište které zjistí zda znak je malé písmeno. (hodnota bude větší
nebo rovno 'a' a menší nebo 'z')
4) - Napište makro pro třetí mocninu (x * x * x). Vyzkoušejte na :
- na_treti(10);
- na_treti(i + 10);
- na_treti(i * 10);
- na_treti(i * j - 3);
(i a j jsou zadaná čísla)
5) - Napište makro pro výpis chybové hlášky a čekání
na stisk klávesy. (použijte čárku)
~Hare Krishna~
-tHE SWINe-