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

© Damig, 2004 – 2023
Koncept

Základní nástroje a pojmy

Protože předpokládám, že někteří čtenáři s nástroji pro překlad programů nikdy nepracovali, musíme si základní nástroje představit a vysvětlit pár základních pojmů. Pokud tyto pojmy znáte, můžete tuto část přeskočit.

Základní nástroje se ovládají z příkazového řádku. V dalších kapitolách se dozvíte jak. Při běžné práci je tímto způsobem většina programátorů nepoužívá. Jsou obvykle volány z nějakého vývojového prostředí nebo pomocí programu make. Přesto je nutné o těchto nástrojích vědět, protože jinak nikdy neporozumíte některým chybovým hlášením, které na vás překladač vybafne.

Nástroje pro překlad programu

Preprocesor

Preprocesor je program používaný v jazycích C a C++ pro zpracovávání maker a symbolických konstant. Je to první program, který při překladu programu zpracovává zdrojový soubor. Preprocesor se obvykle nespouští ručně, ale volá jej překladač nebo jiné nástroje. V open source projektech se zřejmě nejčastěji používá GNU Preprocessor.

Tento program v podstatě pouze provádí textové náhrady symbolů definovaných v textu jako jsou symbolické konstanty a makra. Dále se s jeho pomocí dělá podmíněný překlad. Příkazy pro preprocesor začínají znakem mřížky (#), například #define nebo #ifndef. Příkazy preprocesoru byste, alespoň zpočátku, měli používat co nejméně. Ačkoli to může být mocný nástroj, není to součást programovacího jazyka.

Ačkoli teoreticky mohou být preprocesory používány s kterýmkoli programovacím jazykem, ve skutečnosti se používají jen v malé podmnožině všech programovacích jazyků. Novější jazyky mají syntaktické prostředky, díky kterým preprocesor nepotřebují. Mimo sféru programování se preprocesory dají použít všude, kde se používají textové soubory – například se takto dají zpracovávat CSS a HTML soubory.

Překladač, kompilátor

Překladač (anglicky compiler) je program, který vezme zdrojový text programu a přeloží jej do jazyka stroje, což jsou kódy instrukcí pro počítačový procesor. V případě jazyků C a C++ překladač zpracovává až zdrojový text předzpracovaný preprocesorem.

Výstupem překladače není hned spustitelný program, protože v místech kde se volají podprogramy z jiných modulů a knihoven, nejsou konkrétní adresy těchto podprogramů, ale pouze značky, které se zpracují později při linkování. Souborům, které produkuje překladač se říká objektové soubory (nemá to nic společného s objektově orientovaným programováním). Důležitou funkcí překladače je syntaktická kontrola zdrojového textu programu.

Je vžité, že ze všech zde uváděných nástrojů spouští programátor sám pouze překladač. Ostatní nástroje jsou spouštěny překladačem podle potřeby. Takto to dělají i všechna vývojová prostředí (IDE).

V open source projektech je v současné době zřejmě nejpoužívanějším překladačem GCC, který funguje na obrovském množství dnes používaných platforem a jehož použití si popíšeme v další kapitole.

Schéma překladu zdrojových souborů do spustitelné podoby. V některých implementacích může být assembler vynechán a kompilátor může rovnou produkovat objektové soubory. GCC používá všechny čtyři samostatné programy a postupně je volá v naznačeném pořadí. Pomocí přepínačů lze překlad v každé fázi zastavit a podívat se na mezivýsledek.

Assembler

Jde o program pro překlad jazyka symbolických instrukcí do binárního kódu. Někdy bývá nesprávně zaměňován se samotným jazykem symbolických instrukcí, což je vemi nízkoúrovňový jazyk, kde zdrojový kód tvoří posloupnost zkratek instrukcí procesoru. Při velmi speciálních příležitostech, kdy je potřeba přístup k nejnižším úrovním hardwaru nebo některé optimalizace, lze jazyk symbolických instrukcí kombinovat s kódem v jazyce C.

Při běžném programování se s tím nesetkáte (a ani by to většinou nebylo prospěšné). Je však dobré vědět, že jde o program, který je spouštěn překladačem GCC poté, co tento zpracuje váš zdrojový kód. Teprve assembler vytváří binární kód, který je následně sestavovacím programem sestaven do spustitelného binárního souboru. Překladač GCC používá GNU assembler (manuál).

Linker

Linker je propojovací, resp. sestavovací program sloužící ke spojení samostatně přeložených modulů a knihoven do funkčního celku. Je to poslední nástroj, který je použit, když spustíte překlad programu. Linker do kódu doplní konkrétní adresy podprogramů (statický překlad) nebo kód pro jejich zavolání (při použití dynamických knihoven).

Pokud vytváříme spustitelný program, linker do něj také doplní kód, který se používá při jeho spuštění. Tato část, stejně jako formát použitých statických nebo dynamických knihoven se liší podle operačního systému a hardwarové platformy. Pokud proto chceme vyrobit spustitelný program pro jinou platformu, musíme u jazyků typu C, C++ provést překlad znovu na zvolené platformě nebo musíme překladači a linkeru říci přepínačem, po jakou platformu mají překládat a linkovat.

Interpret

Pojem kompilátor má smysl u kompilovaných jazyků, kam patří C i C++. Výsledkem překladu je pak zkompilovaný, spustitelný program. Dnes se stále častěji setkáte i takzvanými interpretovanými jazyky. Sem patří například Python, Perl nebo PHP.

Hlavní rozdíl je v tom, že interpretovaný jazyk se nepřekládá, ale interpretuje. Pro spuštění Pythonovského programu potřebujete program, který se jmenuje interpret. Ten pak čte zdrojový text programu a rovnou vykonává jednotlivé příkazy.

Tento přístup má několik výhod, ale také hromadu nevýhod. Výhodné je, že interpretované jazyky bývají jednodušší k učení (alespoň zpočátku). Nevýhodou je asi o dva řády pomalejší běh programů oproti jazyku C. Jazyky jako Python, Perl nebo PHP jsou určeny především pro zpracovávání textu. Jazyk C je vhodnější pro rozsáhlé matematické a fyzikální výpočty. Není náhodou, že třeba Python obsahuje rozhraní, pomocí kterého můžete psát kritické části v jazyce C a pak je volat v Pythonovském programu jako knihovní funkce. Podobně se chová například i Java.

Další programátorské nástroje

IDE, vývojové prostředí

IDE znamená Integrated Development Environment, neboli integrované vývojové prostředí. Smyslem těchto prostředí je poskytnout programátorovi všechny vývojářské nástroje tak, aby je měl stále po ruce. Toto prostředí vždy zahrnuje programátorský textový editor, z něhož jsou nějakým způsobem dostupné všechny důležité programátorské nástroje pro překlad a ladění. Ladící program je obvykle integrován do editoru tak, aby mohl během krokování programu přímo zvýrazňovat vykonávané řádky programu a zobrazovat obsah proměnných a paměti. IDE mívají rovněž zakomponován nástroj pro pohodlné hledání v dokumentaci.

Pokročilá profesionální IDE (Eclipse, KDevelop, Borland C++, Visual Studio, a jiné) obsahují i pomocné nástroje pro generování celých částí zdrojového kódu, či vizuální návrh grafického uživatelského rozhraní. Tyto nástroje zvyšují produktivitu profesionálních programátorů. Pokud však s programováním začínáte a chcete se to opravdu naučit, důrazně vám radím tyto pomocníky prozatím nepoužívat! Učit se programovat v profesionálním IDE je jako jezdit v autoškole s formulí. Pro začátečníky je užitečnější začít se střídmějším prostředím, jako je třeba Code::Blocks (a pro úplný začátek si vyzkoušejte čisté nástroje bez IDE, jak jsou popisovány na tomto webu).

Debugger

Je to program pro ladění a hledání chyb v programu. Aby mělo ladění s debuggerem smysl, je obvykle nutné říci překladači, aby do výsledného kódu přidal speciální značky, díky nimž si debugger bude umět spojit konkrétní část binárního kódu programu s konkrétními řádky zdrojového textu.

GNU poskytuje řádkový debugger GDB. Místo ovládání z příkazového řádku se ovšem častěji používají grafické nástavby, které umožňují mnohem pohodlnější a přehlednější používání. Příkladem jsou DDD nebo Kdbg nebo Nemiver. Vývojová prostředí mají debugger integrován přímo v sobě. Například Code::Blocks používá vnitřně GDB, ale krokování je vizualizováno přímo v editoru a obsah paměti a proměnných zobrazuje ve svých oknech.

Laděním a testováním programů se budeme zabývat v jedné z dalších kapitol.

Základní pojmy

Syntaktická chyba

Je to prohřešek proti gramatice programovacího jazyka. Počítač, resp. překladač je matematický stroj a vyžaduje naprosto jednoznačný předpis své činnosti. Ve zdrojovém kódu nesmí být žádné syntaktické chyby, aby bylo jednoznačné, co má překladač dělat. Překladač neví, co chcete naprogramovat, proto se nemůže ani pokoušet o opravu vašich chyb, to musíte udělat sami.

Mezi syntaktické chyby patří především překlepy v konstrukcích jazyka, zapomenutý nebo přebytečný středník, ale může to být také použití nekompatibilních datových typů nebo třeba chybné použití operátorů (např. v jazyce C nemůžete zkoušet násobit textové řetězce). S některými syntaktickými chybami vám dokáže pomoci editor se zvýrazňováním syntaxe, na jiné vás upozorní až překladač.

Sémantická chyba

Je to chyba v logice vašeho programu. Váš program může být syntakticky správný, překladač jej přeloží, linker slinkuje, ale program přesto může fungovat chybně. Za tyto chyby je vždy zodpovědný programátor a je na něm, aby se jich zbavil.

Tyto chyby překladač ani linker obvykle neodhalí. Pokud máte štěstí, může na ně překladač nepřímo upozornit pomocí varovných hlášení při překladu. Moderní překladače jsou v tomto směru poměrně inteligentní, ale stejně vás neupozorní na všechno.

V jazycích C a C++ některé sémantické chyby mohou vést k tomu, že překladač sice program přeloží, ale chybu ohlásí až linker. Tato situace je demonstrována v ukázce 1. A ano, i sémantické chyby mohou být způsobeny překlepy.

Chybové hlášení

Je to zpráva, kterou vám překladač nebo linker sdělují, proč nelze z vašeho kódu vytvořit spustitelný program. Tato zpráva obvykle obsahuje jméno souboru a číslo řádku, kde se chyba nachází. Překladač GCC dokonce v novějších verzích vypisuje přímo řádek kódu se šipkou ukazující, kde si chybu uvědomil. Vývojová prostředí obvykle přebírají hlášení překladače a inkriminovaný řádek kódu vám zvýrazní přímo v editoru.

Pozor! V jazyce C může někdy překladač odhalit chybu na jiném řádku, než je skutečná příčina. Pokud se vám označené číslo řádku nezdá, zkuste hledat chybu i o několik řádků výše. Abyste mohli co nejlépe lokalizovat své chyby, pište na každý řádek zdrojového textu maximálně jeden příkaz. Usnadníte si tím nejen lokalizaci chyb, ale i ladění. Chybové hlášení obsahuje také vysvětlení, co je podle překladače špatně. Čtěte je a snažte se jim porozumět.

Varovné hlášení

Je to upozornění, kterým vám překladač nebo linker dávají najevo, že váš program sice možná jde přeložit, ale že obsahuje podezřelé konstrukce, které mohou naznačovat závažnější problém. Moudrý programátor věnuje varovným hlášením náležitou pozornost. Vaší ambicí by měl být překlad bez varovných hlášení. Dobré porozumění chybovým hlášením vyžaduje znalost angličtiny a především dobrou znalost fungování počítače a logiky programovacího jazyka. Pokud zatím tyto znalosti nemáte, nezoufejte. Není tak těžké se to naučit. Čím více budete programovat, tím to pro vás bude snazší.

Chybových ani varovných hlášení se nebojte a rozhodně je neignorujte ani nevypínejte. Čím více chyb a problémů odhalí překladač, tím lépe pro vás, protože je můžete opravit. Když chyby odhalí koncový uživatel, šéf nebo dokonce učitel při kontrole projektu, bývá to mnohem horší.

$ make
gcc -std=c99 -Wall -pedantic -Wextra -g -lm test.c -o test
test.c: In function ‘main’:
test.c:7:3: warning: implicit declaration of function ‘pritf’ [-Wimplicit-function-declaration]
   pritf("Sčítám čísla:");
   ^
test.c:8:3: warning: format ‘%d’ expects argument of type ‘int’, but argument 3 has type ‘double’ [-Wformat=]
   printf("%d + %d = %d\n", 1, 1.1, 1+1.1);
   ^
test.c:8:3: warning: format ‘%d’ expects argument of type ‘int’, but argument 4 has type ‘double’ [-Wformat=]
/tmp/cccxc0jE.o: In function `main':
/home/martinek/Dokumenty/Projekty/test/21-math/test.c:7: undefined reference to `pritf'
collect2: error: ld returned 1 exit status
make: *** [test] Error 1
Chybové a varovné hlášení při překladu programu. V souboru test.c na třetím znaku sedmého řádku začíná sémantická chyba, překlep v názvu funkce. Překladač to tady přejde varováním a chybové hlášení píše až linker ld, protože funkci s tímto názvem neumí přilinkovat. Na osmém řádku je sémantická chyba při volání funkce printf, protože formátovací značky typově neodpovídají zadaným argumentům (očekává se parametr typu int, ale argument je typu double). Tento kus kódu by překladač uměl přeložit, ale výsledek nemusí odpovídat programátorovu záměru, proto vypisuje varování.