C/C++ (16) - Hlavičkové soubory
Dnes probereme hlavičkové soubory a metodiku práce při psaní projektů z více zdrojových souborů. Příkaz #include <stdio.h> už pro nás přestane být jen vložením tajemného souboru, který nějakým neznámým způsobem zpřístupní některé funkce ze standardní knihovny.
17.3.2005 15:00 |
Jan Němec
| Články autora
| přečteno 41443×
Hlavičkové soubory
V minulých dílech jsme si ukázali některé konstrukce jazyka, které umožní
sestavit program z více zdrojových souborů. Jde zejména o hlavičky funkcí
a deklarace proměnných pomocí klíčového slova extern, dále makra
preprocesoru určená ke globálnímu použití a příkaz #include. Zatím jsme příliš
nepřemýšleli kam nejlépe příslušnou část kódu umístit. Pokud jsme v souboru
main.c užívali funkci z funkce.c, napsali jsme prostě na začátek main.c její
hlavičku. V případě globálních proměnných z funkce.c jsme stejným způsobem
přidali do main.c jejich extern deklaraci. Tímto způsobem se v praxi
neprogramuje. Bylo by jistě nepříjemné pro každou funkci opisovat hlavičku
tolikrát, v kolika souborech je použita.
Při nejčastějším způsobu práce odpovídá každému *.c souboru, který poskytuje
nějaké funkce a globální proměnné, jeden *.h soubor. Název se zpravidla liší
pouze příponou. V tomto *.h souboru jsou hlavičky všech funkcí určených
k použití z ostatních *.c souborů, dále extern deklarace globálních proměnných
a makra preprocesoru, která nějak souvisí s obsahem příslušného *.c souboru.
Hlavičkový *.h soubor obsahuje i definice uživatelských typů, ale to ještě
neumíme. Tento soubor se pak pomocí příkazu #include vkládá na začátek všech
*.c nebo i jiných *.h souborů, které jej potřebují.
Jednoduchý příklad se dvěma *.c soubory by mohl vypadat asi takhle:
funkce.h
funkce.c
void funkce(void) {
/* ... */
}
main.c
#include "funkce.h"
int main(void) {
funkce();
return 0;
}
Všimněte si, že souboru main.c v našem příkladu neodpovídá žádný main.h, neboť
v main.c neposkytujeme žádné funkce ani proměnné k použití z jiných souborů.
Problémy mohou vznikat při cyklické nebo jinak složité závislosti. Mějme
projekt ze tří *.c souborů: main.c, funkce.c a rutiny.c, main.c volá funkce
z obou zbývajících a funkce.c a rutiny.c jsou rovněž na sobě vzájemně závislé.
V tom případě vytvoříme hlavičkové soubory funkce.h a rutiny.h. Všechny tři
*.c soubory můžeme zahájit příkazy
#include "funkce.h"
#include "rutiny.h"
Zatím bude vše fungovat. Horší to bude, pokud vznikne závislost i mezi *.h
soubory. To se může snadno stát, pokud v funkce.h definujeme makro, které
používá jiné makro z rutiny.h. V tom případě umístíme i do funkce.h příkaz
Bohužel v *.c souborech bude rutiny.h inkludován celkem dvakrát, jednou přímo
a jednou prostřednictvím funkce.h. To se nebude líbit překladači. V takto
jednoduchém projektu z několika souborů lze vždy problém nějak obejít,
inkludovat jen některé hlavičkové soubory nebo naopak inkludovat všechny
v pevném pořadí a zrušit #include příkaz ve funkce.h atd.,
ale v praktických složitějších projektech podobné řešení často nelze provést.
Je proto třeba nějak zajistit, aby se obsah *.h souboru vkládal jen jednou a
to i v případě, že je příkaz #include zavolán (přímo nebo zprostředkovaně)
vícekrát. Řešení není příliš obtížné:
rutiny.h
#ifndef _RUTINY_H
#define _RUTINY_H
extern int globalni_promenna;
void rutiny(void);
#endif
Takto se skutečně v praxi programuje. Soubor se vkládá pouze pokud není
definováno makro s charakteristickým jménem, v opačném případě vloží
preprocesor příkazem #include nanejvýš pár prázdných řádek. Pokud se obsah
souboru vkládá, je prvním příkazem definice výše zmíněného makra, které
zabrání opakovanému vložení. Podobným způsobem je ošetřeno určitě alespoň 99%
hlavičkových souborů ze skutečných projektů, zcela jednotná není pouze metodika
pojmenování makra, lišit se mohou například množstvím a umístěním podtržítek
a setkáme se i s makry jako rutinyH a podobně.
Při tomto způsobu práce lze již s úspěchem psát velké projekty s vysokým
stupněm vzájemné závislosti jednotlivých částí. Zdrojové *.c soubory na sobě
záviset (přes volání funkce nebo použití proměnné) i cyklicky, závislost *.h
souborů (použití makra nebo uživatelského typu, příkaz #include) cyklus
obsahovat nesmí. Je běžné, že *.c soubor inkluduje větší množství *.h souborů
stejného projektu. Nikdy by nemělo být podstatné pořadí #include příkazů
a nemělo by ani vadit inkludování *.h souboru, které se nevyužije.
Popsaný způsob, kdy jednomu *.c souboru odpovídá právě jeden hlavičkový
soubor, je určitě nečastější, ale setkáme se i s rozdělením funkcí, proměnných,
maker a typů, které se všechny týkají jednoho *.c souboru, do více *.h,
například
funkce zvlášť, proměnné zvlášť atd. Poměrně běžný je i opačný přístup, kdy
se naopak vše uloží do jediného hlavičkového souboru, který tak pokrývá nějaký
logický celek složený z více *.c souborů. Pokud projekt obsahuje podmíněný
překlad závislý na definici makra (například přítomnost ladícího kódu,
platforma produktu, ...) a nechceme tuto definici předávat jako
parametr překladači, je vhodné všechny volby umístit do jediného hlavičkového
souboru. Ten se pak bude pomocí #include vkládat do všech ostatních souborů,
které obsahují podmíněný překlad.
Příklad pro dnešní díl
Ukážeme si kompletní zdrojový kód projektu ze dvou *.c souborů a jednoho *.h.
Ve funkce.c je funkce, která podle jednoduchého vzorečku sečte n přirozených
čísel, a globální proměnná. Ve funkce.h je hlavička funkce a deklarace
proměnné s extern. Z main.c se jen volá funkce a vypíše hodnota proměnné.
funkce.h
#ifndef _FUNKCE_H
#define _FUNKCE_H
extern int pocet_volani;
/* vrací 1 + 2 + 3 + ... + n */
int suman(int n);
#endif
funkce.c
int pocet_volani = 0;
int suman(int n) {
pocet_volani++;
return (n * (n + 1)) / 2;
}
main.c
#include <stdio.h>
#include "funkce.h"
int main(void) {
printf("1 + 2 + 3 + 4 + 5 = %i\n", suman(5));
printf("počet volání suman() = %i\n", pocet_volani);
return 0;
}
Příklad přeložíme nejjednodušeji
gcc main.c funkce.c -o priklad
Pokračování příště
V příštím dílu se naučíme překládat s pomocí příkazu make. To je
poslední věc, kterou zatím stále děláme jinak než ve skutečných projektech.
Verze pro tisk
| |
Příspívat do diskuze mohou pouze registrovaní uživatelé.
|
|

Vyhledávání software

Vyhledávání článků
20.6.2013 6:58 /MaReK Olšavský Nástup svobodného software začíná i v sousedním Polsku, jehož Ministerstvo Hospodářství spustilo konzultační web. Zdrojové kódy projektu budou vydány pod svobodnou licencí, řešení je založené na Drupalu a Zend Frameworku.
Přidat komentář
20.6.2013 6:58 /MaReK Olšavský Wi-Fi Aliance spustila certifikační program pro 802.11ac, který by měl nabídnout vyšší rychlosti, nižší prodlevy a (opět) přechod na 5GHz.
Přidat komentář
20.6.2013 6:58 /MaReK Olšavský OT: Zájemci o nemoderní a přesto zajímavý programovací jazyk Forth to nemají jednoduché s dostupností literatury. 6 e-knih zdarma o jazyce jež má mnohé principy podobné PostScriptu, snad pomůže některým zájemcům.
Přidat komentář
19.6.2013 6:43 /MaReK Olšavský Svobodný operační systém Contiki nepatří mezi běžně známý software. Výrobce LED „žárovek“ se rozhodl jím osadit výrobky, včetně WiFi konektivity a osvětlení tak bude možné ovládat z chytrého telefonu.
Přidat komentář
19.6.2013 6:43 /MaReK Olšavský Začíná se vyjasňovat v budoucnosti ARMu u AMD. První mikroprocesory by měly být v prodeji v prvním čtvrtletí 2014, jako nástupce současné řady Opteron X. Pokud ale nenabídnou výrazně vyšší výkon, či výrazně menší spotřebu, asi jen největší nadšenci najdou důvod pro přechod od rozšířené a osvědčené platformy x86.
Přidat komentář
18.6.2013 7:01 /MaReK Olšavský Ač se to zdá neuvěřitelné, případ SCO vs. IBM je otevřený. Znovu jej otevřel soud v Utahu, na žádost ještě stále „kopající“ SCO Group. O výsledku případu je těžké polemizovat. Kolem Linuxu se točí velké množství peněz a kdyby dovedlo SCO vybírat poplatky jen za komerční nasazení, možná by mělo příjmy srovnatelně s Red Hatem.
Přidat komentář
18.6.2013 7:01 /MaReK Olšavský Přehrávač Songbird, vyvíjený nad jádrem Gecko, nikdy výrazně neprosadil a od roku 2010 není vyvíjen pro Linux. Eric Wirttman oznámil ukončení projektu k 28. červnu. Zvedne někdo hozenou rukavici, nebo skončí Songbird na smetišti zapomenutých projektů?
Komentářů: 2
17.6.2013 6:54 /MaReK Olšavský Velký přínos pro tržní podíl MariaDB bude znamenat Red Hat Enterprise Linux, který je dalším řadě distribucí přecházejících od MySQL na MariaDB. Vývojáři MariaDB získají, díky síle Red Hatu, významnou pozici na trhu databází.
Komentářů: 1
Více ...
Přidat zprávičku
 Poslední diskuze
19.6.2013 6:19 /
MaReK Olšavský Dement
19.6.2013 5:38 /
MaReK Olšavský Re: ???
18.6.2013 22:21 /
Petr Ježek ???
17.6.2013 18:24 /
Karel Belunek Super!!!
16.6.2013 14:32 /
Petr Ježek jeden ztratil
Více ...
|