Projekty s více soubory
Zdrojový kód lze rozdělit do více souborů.
Jeden soubor (.c) lze chápat jako samostatný modul, který může ostatním
modulům poskytovat některé své funkce a globální proměnné, a ostatní si přitom ponechat soukromé.
Chceme-li, aby byla funkce nebo proměnná platná pouze v souboru, ve
kterém jsou definovány, použijeme k tomu static. Toto klíčové slovo
se píše před definici. Pokud se v jiném souboru nachází definice
proměnné nebo funkce se stejným jménem, jedná se o jinou proměnnou
(nebo funkci).
//
// proměnná private_variable a funkce private_function jsou viditelné
// pouze v souboru, kde se nachází následující definice
//
static int private_variable;
static int private_function(int a, int b)
{
return a + b;
}
Předtím než použíjeme globální proměnnou nebo funkci z jiného
souboru, musíme je deklarovat s klíčovým slovem extern.
U proměnných je povinné, u funkcí je nepovinné, ale je dobré jej tam psát.
//
// následující musí být před použitím dané proměnné nebo funkce
//
extern int variable_in_different_file;
extern int function_in_different_file(int, int);
Linkage
Každý identifikátor (jméno proměnné) má v souboru, kde je definován atribut linkage, který může nabývat tří hodnot.
- no linkage. Tento atribut mají lokální proměnné.
- external linkage. Identifikátor je viditelný pro kód vně modulu, ve kterém je definován. Identifikátor může být s external linkage definován nejvýše jednou v celém programu. Jinde musí být definován s internal linkage nebo bez linkage.
- internal linkage. Identifikátor je viditelný pouze uvnitř
.csouboru, kde je definován.
Vidíme tak, že obyčejné funkce a globální proměnné mají external linkage,
funkce a proměnné definované s klíčovým slovem static mají internal linkage.
Je důležité si uvědomit, že external linkage není identifikátoru přidělen
použitím slova extern.
// file1.c
int x; // external
static int y; // internal
static int sum(int a, int b) // internal
{
int s = a + b; // no linkage
return s;
}
int product(int a, int b) // external
{
int p = a + b; // no linkage
return p;
}
// file2.c
extern int x; // je to proměnná x z file1.c
extern int product(int,int); // funkce product z file1.c
// file3.c
static int x; // toto je OK, má internal linkage
static int product(int, int) // toto je OK, má internal linkage
{
// code
}
// V souboru uz nesmime deklarovat sum nebo product s extern!!!
Klasické dělení na soubory
Definice proměnných a funkcí je v .c souborech
(např. code.c). Funkce a proměnné, které nemají být viditelné vně souboru,
ve kterém jsou definovány, definujeme se static. Funkční prototypy a proměnné, které
mají být viditelné, deklarujeme v souboru .h (např. code.h) s
extern. Soubory, které tyto funkce a proměnné chtějí používat, potom
tento soubor vloží pomocí #include.
Hlavičkový soubor navíc obsahuje i definice typů a maker, které jsou
pro daný .c soubor veřejné nebo potřebné, např. typy, které se
vyskytují ve funkcích a proměnných v daném .h souboru.
Proces překladu
Pro každý .c soubor se provede následující.
-
Spustí se preprocessor.
# pouze preprocesor gcc -E code.c # tiskne na stdout -
Překladač přeloží kód do jazyka symbolických adres.
# preprocesor + preklad do symbolickych adres gcc -S code.c # vytvoří soubor code.s -
Assembler poté kód přeloží do objektového souboru (strojový kód a další informace, např. poskytovaná jména funkcí a proměnných.)
# vytvori objektovy soubour code.o gcc -c code.c -
Linker sloučí vytvořené objektové soubory, spojí je s knihovnami (např. standardní knihovnou) a vytvoří cílený binární soubor (spustitelný soubor, knihovnu).
# code1.o, code2.o jsou objektové soubory gcc code1.o code2.o # volá linker, např. ld
Argumenty funkce main
Funkce main může mít žádný nebo paramatery, její hlavička je potom
int main(int argc, char *arg[]);
Hodnoty parametrů je možné funkci předat při spuštění programu z příkazové
řádky (terminálu, skriptu apod.). Do parametru argc se uloží jejich
počet zvětšený o jedna, jejich hodnoty (jako řetězce) jsou v poli
argv počínaje indexem 1; argv[0] je totiž řetězec odpovídající
spuštěnému programu. (argc je tak velikost pole argv.)
Úkoly
-
Napište program pro výpočet objemu a povrchu válce, pravidelného trojbokého, čtyřbokého a šestibokého hranolu. Parametry výpočtu by mělo být možné předávat programu při spuštění z příkazové řádky. Zdrojový kód programu by měl být rozdělen do 2 modulů. Modul hlavní funkce (soubor
main.c) bude zajišťovat zpracování a případně načtení chybějící parametrů výpočtu, budou z něj volány funkce zajišťující vlastní výpočet a vypisovány vypočítané hodnoty na obrazovku. Druhý modul (souboryvypocet.havypocet.c) pak bude zajišťovat veškeré požadované výpočty. Při řešení úlohy dbejte všech zásad zmíněných na přednášce. zdroj -
Napište v jazyku C jednoduchou knihovnu funkcí pro vykreslování obrázků pomocí znaků (tzv. ASCII art). Knihovna by měla mít tyto vlastnosti:
- Obrázky se budou vykreslovat pomocí plátna - dvojrozměrné matice, která bude obsahovat jednotlivé znaky.
- Vykreslování se tedy neprovádí přímo na výstupu, ale pouze dochází ke změně daného plátna (struktura
Canvas). - Je možné pracovat současně s několika plátny.
- Je možné “vykreslovat” i za hranicí kreslící plochy, tyto body se ale nebudou při zobrazení plátna vykreslovat. Jinými slovy, při pokusu o kreslení mimo plátno nedojde k vyjímce při běhu programu.
Knihovna by měla být samostatným modulem, bude tedy tvořena jedním zdrojovým a jedním hlavičkovým souborem. V knihovně vytvořte strukturovaný datový typ
Canvasa dále definujte tyto funkce:// // Inicializuje plátno na prazdné plátno široké width a vysoké height znaků // void canvas_init(Canvas *c, int width, int height); // // Vrátí znak nacházející na řádku x ve sloupci y // int canvas_get_point(Canvas c, int x, int y); // // Nakreslí obdélník, jehož stěna je tvořena znakem ch // void canvas_draw_rect(Canvas *c, int x, int y, int width, int height, char ch); // // Vyprázdní plátno // void canvas_clear(Canvas *c); // // Vykreslí obsah plátna na standardní výstup/do souboru // void canvas_print(Canvas *c); void canvas_output(Canvas *c, FILE *f); // // Uvolní vnitřní pamět plátna // void canvas_destroy(Canvas *c);