V této kapitole se dozvíte vše, co potřebujete k přeložení jednoduchého programu napsaného v jazyce C nebo C++ pod Linuxem.
Kromě překladačů také při překladu budeme využívat knihovny funkcí, např. knihovnu ncurses (naučíme se i knihovny vytvářet). Kromě samotných knihoven potřebných pro běh programu jsou potřeba i hlavičkové soubory k těmto knihovnám pro překlad. Jsou tam deklarovány funkce z těchto knihoven (např. v Debianu Woody je balíček libncurses5, který obsahuje knihovnu ncurses a balíček libncurses5-dev, který obsahuje zdrojové kódy potřebné pro překlad programů využívajících ncurses). Bude-li nějaký nestandardní balíček pro překlad potřeba, tak se to včas dozvíte. Balíky s potřebnými soubory se bohužel mohou jmenovat v různých distribucích různě.
Poznámka: Některé programy pro překlad potřebují navíc zdrojové kódy běžícího jádra (v adresáři /usr/src/linux). Můžete se s tím setkat při překládání programů z internetu.
Možná by pro vás bylo dobré, kdyby jste se podívali po programovém balíku colorgcc. Jedná se o skripty v jazyce Perl, které spouští překladač gcc (g++) a jeho výstup "obarví" (standardně je všechen výstup z překladače na příkazové řádce bez barvy, colorgcc například chybové hlášení "obarví" červeně atp.). Po jeho instalaci můžete místo příkazu gcc používat colorgcc, nebo ještě lépe, vytvořit symbolický odkaz gcc na colorgcc do některého adresáře, který je v cestě PATH uveden dříve než adresář, ve kterém je překladač gcc. Např:
$ ls /usr/bin/colorgcc
/usr/bin/colorgcc
$ ls /usr/bin/gcc
/usr/bin/gcc
$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games:/home/rimmer/bin
# ln -s /usr/bin/colorgcc /usr/local/bin/gcc
První soubor:
/*------------------------------------------------*/
/* hello.c */
#include <stdio.h>
int main(void)
{
printf("Hello World\n");
return 0;
}
/*------------------------------------------------*/
Druhý soubor:
/*------------------------------------------------*/
/* hello.cpp */
#include <iostream.h>
int main(void)
{
cout << "Hello World\n";
return 0;
}
/*------------------------------------------------*/
První otázka zní, jaký editor použít na vytvoření zdrojového kódu. Odpověď je
vcelku jednoduchá - textový. Nejznámější (a nejlepší) jsou editory vim
a emacs. Oba umí barevně zvýraznit syntaxi jazyka C (emacs od verze 21),
což vám velice usnadní orientaci ve zdrojovém kódu. Jak se tyto editory
používají (a jak zajistit barevné zvýrazňování syntaxe) zde popisovat nebudu.
Vytvořte soubory hello.c a hello.cpp v aktuálním adresáři.
Nyní oba kódy přeložíme. Zdrojový kód hello.c je napsán v jazyce C a tak k jeho překladu použijeme překladač gcc, pro překlad hello.cpp, který je v jazyce C++ použijeme překladač g++.
$ gcc -o hello1 hello.c
$ g++ -o hello2 hello.cpp
$ ls
hello.c hello.cpp hello1 hello2
Za volbou -o následuje název vytvářeného programu (v Unixech se programy neoznačují příponami, ale mají nastavený příznak execute. Ten již po překladu nastaven je). Dále jméno souboru se zdrojovým kódem. Přípony .c (resp. .cpp) jsou překladačem vyžadovány. Pokud by existoval nějaký soubor se stejným jménem, které je uvedeno za volbou -o, byl by bez varování přepsán.
Protože jsou programy v aktuálním adresáři, spustí se za pomoci "tečkové notace" (tečka reprezentuje aktuální adresář, dvě tečky předcházející adresář, to ale určitě už víte).
$ ./hello1
Hello World
Pakliže jste se dostali úspěšně až sem, gratuluji, umíte programovat v Linuxu.
Možná jste tvrdošíjní zastánci grafického rozhraní. Potom vám doporučuji se podívat po programu anjuta. Anjuta umí mimo jiné vytvářet celé projekty, barevnou syntaxi, sdružování těla funkcí, při psaní interaktivně ukazuje požadované argumenty funkcí a mnoho dalšího. Problém je jen v tom, že zde nebudu popisovat použití tohoto programu a vzhledem k tomu že budeme probírat "složitější" operace, jako překládání knihoven programu, různé argumenty překladače atp., bude pro vás mnohem snazší zkoušet si všechno v příkazové řádce. Také to z toho lépe pochopíte.
Poznámka: Ne všechny argumenty, se kterými se můžete setkat, se vztahují k překladači gcc. Překladač totiž spouští i preprocessor (cpp) a assembler (as), které také mají některé specifické volby (například -march=i586 je volba, která assembleru říká, že kód má být přeložen pro architekturu procesoru Intel 586).
Teď si ukážeme použití voleb v tabulce níže. Další volby si probereme později a další najdete v manuálových stránkách k překladači (man gcc).
| Volba | Význam |
|---|---|
| -v | Vypíše verzi překladače. |
| -o program | Určuje název překládaného programu. |
| -Wall | Zobrazování všech varování. |
| -pedantic | Přísnější varování. |
| -ansi | Kontrola standardu ANSI. |
| -O2 | Optimalizace druhého řádu (O je velké ó, ne nula). |
$ gcc -v
Reading specs from /usr/lib/gcc-lib/i386-linux/2.95.4/specs
gcc version 2.95.4 20011002 (Debian prerelease)
Volbu -v můžete použít i při překladu programu. Dočkáte se tak dalšího zajímavého výstupu (např. informace o verzi překladače assembleru). Rozhodně to bude vypadat efektivně, ale pokud se nesnažíte udělat dojem na svojí holku, nemá velký smysl tuto volbu používat :-).
Použití dalších voleb si ukážeme na následujícím zdrojovém kódu.
/*------------------------------------------------*/
/* volby.c */
void main(void)
{
int x, y, z;
char slovo[] = "Ahoj ";
// x nebylo inicializovano
y = x;
for (; y < x + 1; y++)
printf("%s %i\n", slovo, "svete!", x);
return;
}
/*------------------------------------------------*/
Kód je plný chyb. Ukážeme si, jak si s nimi překladač poradí. Nejdříve
přeložíme zdrojový kód nejjednodušším možným způsobem:
$ gcc -o volby volby.c
volby.c: In function `main':
volby.c:5: warning: return type of `main' is not `int'
Kromě jména souboru se zdrojovým kódem jsme při překladu užili jako argument volbu -o, za kterou následuje jméno výsledného programu. Pokud by jste tuto volbu nepoužili, výsledný program by se jmenoval a.out.
Překladači se něco nelíbilo a tak nám hlásí co a kde. Na první řádce hlásí v jakém souboru (překladač může překládat program z mnoha zdrojových kódů) a v jaké konkrétní funkce. Na dalším řádku je vidět varování, které začíná jménem souboru a číslem řádku, kde se vyskytl problém.
Nemusím snad říkat, že by jste měli zdrojový kód upravovat tak, aby varování neobsahoval. Na druhou stranu mohou nastat situace, kdy si překladač myslí, že něco není v pořádku i když je. Raději však vždycky hledejte chybu ve zdrojovém kódu a nesvádějte to na překladač :-).
V programu jsou další chyby, které je překladač gcc schopen odhalit. Pokud chcete, můžete jej o to požádat mnoha volbami, které začínají na W (dvojité w). Například volba -Wformat bude kontrolovat formátovací řetězce (u funkcí printf, scanf atp.).
$ gcc -o volby volby.c -Wformat
volby.c: In function `main':
volby.c:12: warning: int format, pointer arg (arg 3)
volby.c:12: warning: too many arguments for format
volby.c:5: warning: return type of `main' is not `int'
Než vypisovat všechny možné volby pro kontrolu kódu, je lepší použít volbu -Wall, která v sobě zahrnuje téměř všechny volby související s výpisem varovných hlášení. Které všechny volby to jsou (a k čemu) se dočtete v manuálové stránce k překladači gcc (také k g++ a cc). Ještě přísnější varování dostanete při současném použití volby -pedantic.
$ gcc -o volby -Wall -pedantic volby.c volby.c:5: warning: return type of `main' is not `int' volby.c: In function `main': volby.c:12: warning: implicit declaration of function `printf' volby.c:12: warning: int format, pointer arg (arg 3) volby.c:12: warning: too many arguments for format volby.c:6: warning: unused variable `z'Projděme si jednotlivé řádky chybových hlášení. První říká, že funkce main nemá návratovou hodnotu deklarovanou jako typ int (přestože v Linuxu ji mít musí). O tom jsme již hovořili výše. Další řádek nás informuje, že byly ve funkci main nalezeny chyby (následuje jejich výpis). Třetí varování oznamuje, že překladač nenašel deklaraci funkce printf. To napravíte začleněním knihovny stdio.h (kde je funkce stdio.h deklarována) do zdrojového kódu (direktivou #include <stdio.h>). Čtvrté varování oznamuje, že funkce printf (resp. její formátovací řetězec) očekává jako třetí argument proměnnou typu int, ale proměnná je typu ukazatel. (Prvním argumentem je vlastní formátovací řetězec, druhým proměnná slovo a třetím řetězec "svete!", který překladač umístí někam do paměti a funkci předá jako argument ukazatel (pointer) na tento řetězec). Páté varování informuje o tom, že funkce printf má více argumentů, než kolik jich očekává formátovací řetězec. Poslední varování oznamuje, že proměnná z nebyla použita (a je tedy v programu zbytečně).
Teď se podíváme na volbu -ansi. Díky této volbě bude překladač kontrolovat, zda zdrojový kód odpovídá normě ANSI. Tato volba je velmi zajímavá, ale v Linuxu mnohdy nepoužitelná. Problém je v tom, že ne všechny knihovny, které při vytváření programů budete používat, tuto normu splňují. A tak vám volba program využívající tyto knihovny neumožní přeložit. Na příkladu si ukážeme kdy se může tato volba přece jen hodit.
$ gcc -ansi volby.c -o volby volby.c: In function `main': volby.c:8: parse error before `/' volby.c:5: warning: return type of `main' is not `int'
Jistě víte, že komentáře začínající dvěma lomítky jsou v normě definovány pro jazyk C++, nikoliv C. Proto program ohlásil chybu na řádku 8 a překlad programu tím skončil bez úspěchu. Když sem vytvářel programy v Debianu Woody (pomocí překladače který využívám i zde), překladači komentář za dvěma lomítky nikterak nevadil. Bohužel jsem později zjistil, že překladače z jiné distribuce při použití volby -Wall vypisují u každého takového komentáře varování a to nebylo moc hezké. K odstranění této a všech dalších potenciálních chyb jsem proto použil volbu -ansi. (Kdyby šlo jenom o nalezení komentáře za dvěma lomítky, asi by bylo lepší použít linuxový program grep). Mimo jiné jsem tak odhalil některá makra, která byla součástí mého preprocesoru, ale nebyla součástí ANSI normy. Ale jak už jsem upozorňoval, není vždy možné chtít, aby bylo možné celý program přeložit s touto volbou.
Podívejme se, zda bude mít optimalizace nějaký vliv i při překladu našeho malého programu.
$ gcc -o volby1 -Wall volby.c volby.c:5: warning: return type of `main' is not `int' volby.c: In function `main': volby.c:12: warning: implicit declaration of function `printf' volby.c:12: warning: int format, pointer arg (arg 3) volby.c:12: warning: too many arguments for format volby.c:6: warning: unused variable `z' $ gcc -o volby2 -Wall volby.c -O3 volby.c:5: warning: return type of `main' is not `int' volby.c: In function `main': volby.c:12: warning: implicit declaration of function `printf' volby.c:12: warning: int format, pointer arg (arg 3) volby.c:12: warning: too many arguments for format volby.c:6: warning: unused variable `z' volby.c:6: warning: `x' might be used uninitialized in this function $ ls -l volby1 volby2 -rwxr-xr-x 1 rimmer users 4869 bře 12 13:16 volby1 -rwxr-xr-x 1 rimmer users 4853 bře 12 13:16 volby2Kromě toho, že optimalizovaný program je o pár bajtů menší (u větších programů by byla optimalizace výraznější) má optimalizace ještě jeden efekt - varování o použití neinicializované proměnné x. Toto varování způsobuje volba -Wuninitialized, která je schovaná ve volbě -Wall a která má efekt jen tehdy, když je zapnutá optimalizace kódu.
Program strip je první z pomocných nástrojů, který probereme. A k čemu je? Výsledný program obsahuje kromě instrukcí také symboly, které se mohou hodit např. pro ladění programu, informace o překladači, o zdrojovém kódu atp. Některé z těchto pro běh programu zbytečných informací lze odstranit pomocí programu strip, zmenšit tak program a tím jej zrychlit. Více o programu strip se dočtete v manuálové stránce.
$ strip volby1 volby2 $ ls -l volby1 volby2 -rwxr-xr-x 1 rimmer users 3076 bře 13 22:14 volby1 -rwxr-xr-x 1 rimmer users 3060 bře 13 22:14 volby2