LINUXSOFT.cz Přeskoč levou lištu
Uživatel: Heslo:  
   CZUKPL

> 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

void funkce(void);

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

#include "rutiny.h"

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

pridej.cz

 

DISKUZE

C/C++ 17.3.2005 21:29 jan
  L Re: C/C++ 18.3.2005 07:37 Jan Němec
    L Re: C/C++ 19.3.2009 12:46 Vojtěch 'Malajda' Malášek
      L Re: C/C++ 19.3.2009 15:50 Aleš Hakl




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 ...

ISSN 1801-3805 | Provozovatel: Pavel Kysilka, IČ: 72868490 (2003-2013) | mail at linuxsoft dot cz | Design: www.megadesign.cz | Textová verze | zákony online