Jak na pro­jek­ty v ja­zy­ce C

© Damig, 2004 – 2016
Koncept

Překládáme

Tato kapitola se nevěnuje origami, jak by k tomu mohl svádět nadpis, ale překladu počítačového programu ve smyslu překladu z jednoho jazyka do druhého. Napíšeme-li program v některém z programovacích jazyků, neznamená to, že mu bude počítač okamžitě rozumět. Programovací jazyky slouží pro nás, programátory. Proto jsou blízké přirozenému jazyku, případně jazyku matematiky, abychom mu rozuměli a byli schopni vyjadřovat své záměry. Počítač ale takovým jazykům přímo nerozumí. Někdo, respektive něco mu to musí přeložit do jeho jazyka – jazyka stroje, který je tvořen kódy instrukcí a daty, které mají tyto instrukce zpracovat. K tomuto slouží překladač, jehož použití si zde popíšeme.

Následující ukázky jsou demonstrovány v unixové konzoli. Ve Windows bude práce vypadat skoro stejně. Na drobné rozdíly upozorním.

Překlad programu

Dostali jsme se do bodu, kdy už si konečně můžeme zkusit přeložit první program v jazyce C. V tuto chvíli to budeme provádět ručně, ale hned v další kapitole si ukážeme, že tyto věci lze zautomatizovat. V tuto chvíli vám bude stačit obyčejný textový editor (MS Word, OO Writer a podobné na to fakt nejsou vhodné). Pro tuto chvíli nebudete potřebovat ani žádné vývojové prostředí. Stačí opravdu jen editor a příkazový řádek (textová konzole).

Pokud vám není příliš jasný význam pojmů jako překladač, linker a dalších, přeskočte nejdříve na kapitolu Základní nástroje a pojmy. V následujících ukázkách budu používat překladač GCC. Budu tedy předpokládat, že jej máte nainstalovaný. Chcete-li se o něm dozvědět něco více, podívejte se na sekci o překladači GCC a na sekci popisující jeho užitečné přepínače.

/* Projekt: První projekt */
#include <stdio.h>

int main(void)
{
  printf("Tě péro sombréro!\n");
  return 0;
}
Náš první jednoduchý program pro vyzkoušení práce s překladačem. Uložte si jej do souboru helloword.c.

Nejprve tedy napište klasický program Hello world a uložte jej do textového souboru helloworld.c. Kód takového triviálního programu si můžete prohlédnout a zkopírovat z ukázky 1. Není to nic světoborného, ale pro začátek nám to bude stačit.

Z příkazové řádky tento soubor přeložíme příkazem gcc helloworld.c. Překladač vytvoří spustitelný soubor a.out a uloží jej do aktuálního adresáře. Tento nově vzniklý program po spuštění vypíše jeden řádek textu, viz ukázka 2. Ve Windows se tento soubor bude jmenovat a.exe a budete jej pochopitelně spouštět pod tímto jménem.

$ ./a.out
Tě péro sombréro!
Spuštění prvního programu v adresáři, kde jste program překládali. Pozor v unixových systémech je potřeba spouštět i s tou tečkou a lomítkem před názvem programu. Ve Windows tečku a lomítko vynechejte a spusťte prostě a.exe.

Při tomto způsobu překladu jsme ponechali implicitní nastavení překladače. Není ovšem dobré spoléhat na standardní nastavení. Překladač v tomto stavu nehlídá všechny chyby, které by se v programu mohly vyskytnout a umožňuje používat různá nestandardní rozšíření jazyka (což my nechceme). Zvláště začátečníci by měli překládat programy s poněkud přísnějším nastavením. Překladač potom bude vypisovat více varovných zpráv. Může se to sice zdát otravné, ale uvažte, že čím více chyb odhalí překladač už při překladu, tím více práce to ušetří při ladění.

V ukázce 3. je překladač spuštěn tak, aby hlídal dodržování normy jazyka ISO C99 a zároveň vypnul všechna nestandardní rozšíření jazyka, vypisoval varování při všech problémech, které odhalí, a generoval informace pro ladící program. Zároveň zde říkáme překladači, aby vyrobil spustitelný soubor hello (ve Windows s koncovkou .exe) namísto souboru a.out. Toto je minimální nastavení, které byste měli při překladu používat, chcete-li používat čistý jazyk C a vytvářet přenositelné programy. Budete-li časem používat nějaké vývojové prostředí, ověřte si, že máte pro překlad zapnuty právě tyto přepínače.

Chcete-li překládat s ještě přísnějším nastavením, zkuste to tak, jak je to předvedeno v ukázce 4. V tomto případě budou veškerá případná varování automaticky prohlášena za chyby a k překladu dojde jen v případě, že je vše opravdu v pořádku.

V prostředí Windows se překladač používá stejným způsobem. Rozdíl je v tom, že prostředí Windows není case-sensitive, takže je jedno, jestli soubory pojmenováváme velkými nebo malými písmeny. Aby byly programy přenositelné, je dobré si zvyknout pojmenovávat je výhradně malými písmeny. Vyhnete se tím případným problémům při přenosu programu do jiných operačních systémů.

$ gcc -std=c99 -pedantic -Wall -g helloworld.c -o hello
Překlad, který zapne specifickou normu (-std), vypne rozšíření (-pedantic), zapne výpis chybových hlášení (-Wall) a generování ladících informací (-g). Výsledek uloží do souboru se zadaným jménem (-o).
$ gcc -std=c99 -pedantic-errors -Wall -Werror -g helloworld.c -o hello
Překlad, kde se odchylky od normy (-std) považují rovnou za chyby (-pedantic-errors). Stejně tak jsou všechna varování rovnou prohlášena za chyby (-Werror).

Překlad s matematickou knihovnou

Chcete-li ve svém programu používat matematické funkce pracující s desetinnými čísly, musíte v prvé řadě do programu vložit hlavičkový soubor math.h. Další užitečné konstanty jsou v hlavičkovém souboru float.h. Příklad takového jednoduchého programu je v ukázce 5.

Při překladu takového programu je nutné přidat přepínač -lm, který zajistí přilinkování matematické knihovny, viz ukázka 6. Bez této knihovny sice půjde program přeložit, ale nepůjde jej sestavit (slinkovat).

Tedy... Předchozí odstavec beze zbytku platí pro starší verze překladače GCC. U novějších (já jsem to zaznamenal u verze 4.8) se už matematická knihovna k programu linkuje automaticky a přepínač -lm už není při překladu potřeba používat.

Tento přepínač vlastně nepatří překladači, ale linkovacímu programu. Není potřeba jej volal ručně. Program gcc si linker zavolá sám a přílušné přepínače mu předá. Ve starších verzích překladače GCC bylo nutné parametry pro linker zapisovat až jako úplně poslední přepínače v řadě. U novějších už to nění potřeba, ale kdybyste linkovali více knihoven, může záležet na pořadí, v jakém je budete linkovat.

Standardní knihovna jazyka C se k programu linkuje automaticky bez nějakého přepínače. Důvod proč jsou matematické funkce v samostatné knihovně je v podstatě historický. Jazyk C je od počátku zamýšlen jako multiplatformní a to i pro takové procesory, které vyšší matematické funkce neumí (třeba v automatické pračce nejsou potřeba). Na takových zjednodušených platformách se musí práce s desetinnými čísly složitě emulovat a matematická knihovna je pak příliš velká na to, aby se linkovala k programům, které ji vůbec nepotřebují.

U nových verzí překladače GCC se matematická knihovna linkuje automaticky, což ale nemusí platit pro tyto jednodušší platformy. Stejně tak to nemusí platit pro jiné překladače než GCC.

Jiné knihovny se linkují podobným způsobem jako matematická knihovna. Matematická knihovna je však obvykle první linkovanou knihovnou, se kterou začínající programátor přijde do styku, proto ji zde speciálně zmiňuji. Používání knihoven je podrobněji probíráno v kapitole Moduly a knihovny.

#include <stdio.h>
#include <math.h>
#include <float.h>

int main(void)
{
  printf("log(%.*e) je %.*e.\n", DBL_DIG, DBL_MAX, DBL_DIG, log(DBL_MAX));
  return 0;
}
Program využívající funkci z matematické knihovny. Vypisuje výsledek funkce logaritmus na maximální smysluplný počet platných desetinných číslic.
$ gcc -std=c99 -pedantic -Wall -g -lm matematika.c -o matematika
Překlad programu využívajícího matematickou knihovnu vyžaduje přepínač -lm pro linker.

Překladač GCC

GCC znamená GNU Compiler Collection. Jak název napovídá, jde o kolekci programů určených pro překlad programů napsaných v různých jazycích. V současné verzi (4.8) je podporováno několik programovacích jazyků (C, C++, Objective C, Objective C++, Assembler, Go, Ada, Java, Fortran a několik dalších). Tento překladač funguje na většině hlavních softwarových i hardwarových platforem (Unix, Windows, MacOS; Intel, PPC, Sparc, různé jednočipy, a jiné).

Nejdůležitějšími programy jsou překladač, preprocesor, assembler a sestavovací program (linker). Člověk však naštěstí nemusí znát konkrétní jména těchto programů. Stačí volat pouze překladač (gcc pro jazyk C, g++ pro jazyk C++) a ten potom podle použitých parametrů zavolá ty správné programy ve správném pořadí.

Vybrané užitečné přepínače gcc

Překladač gcc rozumní obrovskému množství přepínačů. Jejich vyčerpávající seznam naleznete v manuálové stránce programu gcc. My si zde ukážeme jen pár z nich, které zřejmě můžete použít ve svých prvních projektech.

Přepínače kontrolující výstup

Parametr Význam Poznámka
-o soubor Uloží výstup do spustitelného souboru daného jména. Bez tohoto přepínače překladač vytváří soubor, který se jmenuje a.out (Linux) nebo a.exe (Windows).
-E Překlad se zastaví po zavolání preprocesoru. Pokud nepoužijete zároveň parametr -o, kód programu upravený preprocesorem se vypíše na standardní výstup. Výsledkem překladu je textový zdrojový soubor, ve kterém jsou rozgenerovány všechny příkazy preprocesoru. Toto je užitečné, když se chcete podívat, zda se správně rozgenerovala makra.
-S Překlad se zastaví po vygenerování kódu pro assembler. Toto používají zvrhlíci, které baví rochnit se ve změti instrukcí. Občas je možné ručně změnit pár instrukcí v rámci optimalizací, ale většinou assemblerový kód využijete tehdy, když chcete před někým dělat machra. ;-) No dobrá, občas je to možné použít i pro výuku jazyka symbolických instrukcí. Výsledkem překladu je textový soubor se zdrojovým textem programu v jazyce symbolických instrukcí. Pozor! Pokud se vám pomocí tohoto povede sbalit životní partnerku, vystavujete se nebezpečí, že vaše děti budou mít ve škole přezdívky jako šprt, integrál nebo EAX!
-c Překlad se zastaví po překladu do binárního kódu a už neprovede linkování. Tento přepínač se používá při odděleném překladu více zdrojových souborů, kdy se linker volá samostatně až po zkompilování všech modulů. Výsledkem překladu je binární objektový soubor. Je možné jej použít pro syntaktickou kontrolu zdrojového souboru. Je to pak rychlejší, protože se neprovádí linkování, překladač pouze vypíše chyby a varovné zprávy. Pozor ovšem na skutečnost, že některé chyby (např. použití nedefinované funkce) odhalí jako chybu až linker.
-v Vypíše parametry, se kterými byl překladač sám přeložen, plus jeho verzi. Z těchto údajů se dají vyčíst zajímavé údaje, jako kde překladač očekává statické, či dynamické knihovny, nebo jestli zvládá vícevláknové aplikace. Podobnou funkci má i přepínač -###. Podrobnosti viz manuálovou stránku gcc.
--version Vypíše verzi překladače.  
-MM Místo předkladu vypíše na standardní výstup pravidla pro program make se závislostmi jednotlivých modulů. Toto se používá při automatickém generování souborů Makefile. Při generování se vynechají všechny závislosti na systémových knihovnách a preprocesor jde jen po lokálních hlavičkových souborech a modulech. V tomto případě se neprovádí překlad, pouze se volá preprocesor. Přepínače pro varování nebo pro zapínání normy jazyka zde nemají smysl a ignorují se. Aby to fungovalo správně je nutné mít ve všech modulech vloženy hlavičkové soubory s odpovídajícím rozhraním

Volba dialektu jazyka C/C++

Parametr Význam Poznámka
-std=c99, -std=c11 Zapíná specifikaci jazyka C podle normy ISO z roku 1999, tedy ISO C99, respektive normu ISO C11 z roku 2011. Tento přepínač rozhodně používejte! Nejlépe spolu s přepínačem -pedantic. Zajistíte tím, že program bude přenositelný mezi moderními překladači na množství platforem a vyhnete se nechtěnému použití nestandardních rozšíření překladače GCC. Nepoužívat u programů psaných v C++.
-std=c++98, -std=c++03 Zapíná specifikaci jazyka C++ podle normy ISO z roku 1998, respektive 2003 Pouze pro jazyk C++. Nepoužívat u Céčkových programů!
-ansi Zapíná specifikaci jazyka C podle normy ANSI. Tento přepínač je ekvivalentní přepínači -std=c89. Tento přepínač je použitelný, když chceme, aby byl program kompatibilní se starší verzí překladače. Raději používejte -std=c99.

Varování, hlášení chyb

Parametr Význam Poznámka
-Wall Zapíná produkci varovných zpráv pro téměř všechny aspekty, které překladač umožňuje hlídat. Existuje velké množství parametrů -W..., které dovolují určovat konkrétní aspekty jazyka, které má překladač hlídat. Existují aspekty, které se pomocí -Wall nezapnou, ale ty se týkají výhradně jazyka C++. Velmi důležité pokud se chceme vyhnout množství nepříjemných chyb.
-pedantic Hlídá, jestli program striktně dodržuje normu ISO C nebo ISO C++ zadanou parametrem -std. Pokud program normu nedodržuje, vypisuje se varování. Velmi důležité pro psaní přenositelných programů.
-Wextra, -W Produkuje některá další užitečná varování. Zapnutím tohoto přepínače přinutíme překladač, aby prováděl (částečnou) sémantickou kontrolu kódu. Přepínač -W je zastaralý. Nový název přepínače -Wextra je výstižnější a nehrozí jeho záměna s přepínačem -w.
-w Potlačí veškerá varovná hlášení. Pozor! Má naprosto jinou funkci než přepínač -W (velké písmeno), snadno se s ním plete a může způsobovat falešný pocit, že překládaný program je v naprostém pořádku.
-Werror Všechna varování interpretuje jako chyby. Pro začátečníky je velmi užitečné mít jej zapnutý.
-pedantic-errors Jako -pedantic, ale místo varování produkuje rovnou chybová hlášení. Pro začátečníky je velmi užitečné mít jej zapnutý.
Zdá se, že některé implementace GCC pod Windows používají v hlavičkových souborech nestandardní rozšíření, takže tento přepínač zde nejde použít.

Informace pro debugger, optimalizace

Parametr Význam Poznámka
-g, -g1, -g2, -g3 Překladač bude do výsledného kódu přidávat pomocné ladící informace pro debugger. Pokud při překladu není zvolen nějaký z těchto přepínačů, bude debugger schopen zobrazovat pouze informace na úrovni assembleru – nebude schopen zobrazovat názvy funkcí a proměnných. Číslo za přepínačem udává, jak podrobné ladící informace se budou generovat. Přepínač -g je ekvivalentní přepínači -g2. Tyto přepínače lze použít zároveň s přepínačem -O, ale většinou to nemá smysl. U starších verzí překladače to může působit potíže.
-ggdb, -ggdb1, -ggdb2, -ggdb3 Podobné jako u přepínače -g, ale ladící informace budou optimalizovány pro debugger gdbg. Existují i jiné přepínače, kterými se dá zapnout optimalizace pro jiné debuggery, ale gdb je nejběžnější. Více viz manuálovou stránku gcc.
-O, -O1, -O2, -O3 Zapne optimalizaci výsledného kódu. Číslo za přepínačem specifikuje úroveň optimalizace. Přepínače -O-O1 jsou ekvivalentní. Čím vyšší úroveň optimalizace zvolíte, tím delší dobu bude trvat překlad. Tento přepínač používejte společně s přepínačem -g jenom když víte co děláte. Zvláště na pomalých strojích je vhodné zapínat optimalizace pouze při konečné kompilaci, ne při běžném psaní kódu. (Platí pro jednoduché projekty.)
-O0 Vypne optimalizace. Tento přepínač je implicitně zapnut.
-p, -pg Překladač bude do výsledného kódu přidávat informace použitelné pro profilovací analýzu programem prof nebo gprof (pro -pg). Rozhodnete-li se jej použít, je nutné jej použít nejen při překladu, ale i při linkování programu. Profilování je statistická analýza běhu programu, ze které se dá zjistit, ve které funkci program tráví nejvíce času. Pokud vás svrbí prsty a rádi byste svůj program optimalizovali, nejprve proveďte profilovací analýzu. Optimalizovat funkci, která se vyvolá dvakrát během celého běhu programu, je mrhání vaším časem.