Argumenty gcc a g++ a clang
Překladače gcc / clang mají velké množství přepínačů. Předkladač clang je kompatibilní s gcc, takže všechny zde probírané volby platí stejně jak pro gcc, tak pro clang. Vybral jsem pro vás ty nejdůležitější a najezajímavější.
Použití argumentů programu gcc se neliší od používání argumentů jiných programů v Linuxu. Některé volby očekávají další argument (např. volba -o očekává název výsledného programu), jiné nikoliv.
Argumenty musí být od sebe oddělené. Volba -dr je něco jiného než volby -d -r (většina jiných linuxových programů by v těchto dvou zápisech rozdíl nedělala).
Na pořadí zápisu argumentů nezáleží. Mezi očekávané argumenty překladačem patří název/názvy zdrojových kódů.
Ne všechny argumenty, se kterými se můžete setkat, se vztahují přímo k
překladači gcc. Překladač totiž spouští další pomocné programy,
například preprocessor (cpp), nebo assembler
(as), které také mají některé specifické volby.
Třeba -march=i586
je volba, která assembleru říká, že kód má být přeložen pro architekturu
procesoru Intel 586.
Tabulka argumentů gcc
Tady si můžete prohlédnout tabulku základních voleb gcc. Další volby
najdete v manuálových stránkách k překladači (man gcc
).
Některé volby jsem už probral v minulé kapitole, jiné proberu podrobněji později.
Volba | Význam |
---|---|
-v | Vypíše verzi překladače a volby, se kterými byl přeložen. |
-o program | Určuje název výsledného přeloženého programu. |
-Wall | Zobrazování všech varování (warning all). |
-ansi | Kontrola standardu ANSI c90. Zakazuje některá GNU/Linux rozšíření, takže to v tomto kurzu určitě nepoužívejte :-) |
-pedantic | Přísnější konstrola standardu. (To taky nepoužívejte.) |
-O2 | Optimalizace druhého řádu. (O je velké ó, ne nula.) |
-x c++ | Následující zdrojáky se pokusí přeložit jako c++ soubory. K tomu je potřeba ještě přilinkovat standardní knihovnu pro c++ |
-lknihovna | Přilinkuje nějakou knihovnu. Knihovna se hledá dle jména ve standardních adresářích a adresářích přidaných pomocí -L. |
-lstdc++ | Přilinkuje standardní knihovnu jazyka C++ stdc++ (konkrétně to může být např. /usr/lib64/libstdc++.so.6). Hodí se k volbě -x c++ |
-Ldir | Přidá adresář dir do cest, které prohledává volba -l |
-g | Do programu se přidávají informace pro ladění. Informace je
možné později z programu odstranit pomocí programu strip . |
První přepínač (-v) vypíše informace o překladači. Z následujícího výpisu uvidíte jakou verzi překladače jsem používal při výkladu. Označení verze překladače gcc a g++ je totožný.
Using built-in specs.
COLLECT_GCC=/usr/bin/gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/i586-suse-linux/4.8/lto-wrapper
Target: i586-suse-linux
Configured with: ../configure --prefix=/usr ...
Thread model: posix
gcc version 4.8.1 20130909 [gcc-4_8-branch revision 202388] (SUSE Linux)
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 šéfa, nemá to velký smysl :-). Smysl to má jen tehdy, když se chcete dozvědět podrobnosti o tom, jaké všechny knihovny jsou „přilinkovány“ k vašmu programu (ale k tomu se ještě dostanu).
Použití některých voleb ukáži na následujícím zdrojovém kódu.
Kód je plný chyb. Uvidíte, jak si s nimi překladač poradí. Nejdříve přeložte zdrojový kód nejjednodušším možným způsobem:
- $ gcc -o volby volby.c
- volby.c: In function ‘main’:
- volby.c:12:2: warning: incompatible implicit declaration of built-in function ‘printf’ [enabled by default]
- printf("%s %i\n", slovo, "svete!", x);
- ^
Překladači se něco nelíbilo a tak hlásí co a kde. Na první řádce
hlásí v jakém souboru a v jaké funkci. Na dalším řádku je vidět varování, které
začíná jménem souboru, číslem řádku a „sloupce“,
kde se vyskytl problém
(funkce printf()
na dvanáctém řádku je odsazena tabulátorem,
proto, ač to třeba nevypadá, začíná skutečně druhým znakem řádku).
#include <stdio.h>
,
kde je funkce printf()
deklarována.
A teď s volbou -Wall:
volby.c:4:6: warning: return type of ‘main’ is not ‘int’ [-Wmain]
void main(void)
^
volby.c: In function ‘main’:
volby.c:12:2: warning: implicit declaration of function ‘printf’ [-Wimplicit-function-declaration]
printf("%s %i\n", slovo, "svete!", x);
^
volby.c:12:2: warning: incompatible implicit declaration of built-in function ‘printf’ [enabled by default]
volby.c:12:2: warning: format ‘%i’ expects argument of type ‘int’, but argument 3 has type ‘char *’ [-Wformat=]
volby.c:12:2: warning: too many arguments for format [-Wformat-extra-args]
volby.c:6:15: warning: unused variable ‘z’ [-Wunused-variable]
int x, y, z;
^
volby.c:10:7: warning: ‘x’ is used uninitialized in this function [-Wuninitialized]
y = x;
^
A ještě se podívejte na clang. Ten vám občas i poradí, jak se warování zbavit.
volby.c:4:1: warning: return type of 'main' is not 'int' [-Wmain-return-type]
void main(void)
^
volby.c:4:1: note: change return type to 'int'
void main(void)
^~~~
int
volby.c:12:2: warning: implicitly declaring library function 'printf' with type 'int (const char *, ...)'
printf("%s %i\n", slovo, "svete!", x);
^
volby.c:12:2: note: include the header <stdio.h> or explicitly provide a declaration for 'printf'
volby.c:12:27: warning: format specifies type 'int' but the argument has type 'char *' [-Wformat]
printf("%s %i\n", slovo, "svete!", x);
~~ ^~~~~~~~
%s
volby.c:12:37: warning: data argument not used by format string [-Wformat-extra-args]
printf("%s %i\n", slovo, "svete!", x);
~~~~~~~~~ ^
volby.c:6:15: warning: unused variable 'z' [-Wunused-variable]
int x, y, z;
^
volby.c:10:9: warning: variable 'x' is uninitialized when used here [-Wuninitialized]
y = x;
^
volby.c:6:10: note: initialize the variable 'x' to silence this warning
int x, y, z;
^
= 0
6 warnings generated.
Linkování knihoven
Ve zdrojových kódech často používáte hlavičkové soubory standardních
kihoven, jako je například <stdio.h>
,
nebo <math.h>
. Zdrojové kódy těl funkcí (definice)
v hlavičkových souborech nejsou. Ty jsou ve zdrojových souborech,
které běžně v distribuci nainstalované nejsou
(většina distribucí však umožňuje přidat repozitáře na zdrojové kódy
k většině programů a knihovnám, které distribuce obsahuje).
Těla funkcí definovaných v těchto hlavičkových souborech
jsou už přeložené v knihovnách.
Například funkce printf()
je ve standardní knihovně jazyka C
(libc), zatímco sqrt()
(odmocnina) je v knihovně matematických
funkcí libm
.
/lib/libm.so.6
.
Knihovna libc
je přilinkovávána automaticky,
takže se o ni nemusíte starat. U ostatních knihoven už musíte překladači říct,
že chcete, aby je přilinkoval (tj. připojil k výslednému programu).
Kdyby totiž přilinkovával všechny knihovny, které máte nainstalované,
překlad by se neúměrně prodloužil, protože by se museli všechny knihovny pročítat
(hledají se v nich funkce, které používáte).
K přidání knihovny k linkování slouží volba -lknihovna
.
(Bez mezery mezi -l
a názvem knihovny.)
Jméno knihovny se uvádí bez úvodního lib
, tj. například
libm
připojíte k programu pomocí -lm
.
Tuto volbu můžete použít pro více knihoven. Pokud existuje funkce téhož ména ve více knihovnách které chcete přilinkovat, použije se ta první, která se najde (tj. ta, která je v knihovně, kterou uvedete na příkazovém řádku v argumentech dříve).
Když říkám, že překladač knihovnu přilinkuje, tak to není tak úplně pravda.
Linkování knihoven nedělá překladač ale linker, což je jiný program
(ld
), který je ovšem volán automaticky překladačem.
Linker prohledá všechny objekty (vámi přeložený zdrojáky a knihovny) a spojí
jejich symbolické reference v jeden spustitelný soubor - výsledný program.
Program pak nemůže bez „přilinkovaných“ knihoven běžet.
Ale jen bez těch, ze kterých skutečně nějaké funkce používá
(ostatní přilinkovány nejsou).
Jako příklad použiji zmíněnou matematickou knihovnu a její funkce
pow()
(mocnina) a sqrt()
(odmocnina).
- /*------------------------------------------------*/
- /* 03volby/math.c */
- #include <stdio.h>
- #include <math.h>
- {
- float c;
- }
- /*------------------------------------------------*/
Nejdříve pokus bez přilinkování knihovny /lib/libm.so.6
.
/tmp/ccfMMXzR.o: In function `main':
math.c:(.text+0x2f): undefined reference to `pow'
math.c:(.text+0x59): undefined reference to `pow'
math.c:(.text+0x75): undefined reference to `sqrt'
collect2: error: ld returned 1 exit status
Všimněte si, že si nestěžuje gcc
ale linker ld
(collect2: error: ld returned 1 exit status).
Soubor /tmp/ccfMMXzR.o
je objekt, který byl vytvořen
překladem. Jde o dočasně vytvořený soubor, který je hned smazán, tak ho nehledejte.
A teď přilinkuji matematickou knihovnu. Všimněte si, že se nezadává
absolutní cesta ke knihovně, ale její název, a to ještě bez úvodního lib
a bez přípony s číslem verze (.so.6
). Linker si vše dohledá
sám, čímž se přidávání knihoven velmi zjednodušuje.
$ ./math
c = sqrt(9 + 16) = 5.000000
Soubor /lib/libm.so.6
je ve skutečnosti jen symbolický odkaz
na aktuální verzi knihovny, která má jiné („podrobnější“) číslo
verze. Teoreticky to funguje tak, že pokud se nezmění hlavní číslo verze,
bude váš program stále fungovat s novějšími (opravenými/doplněnými) knihovnami.
Pokud v knihovně dojde k změně, která by nebyla kompatibilní, změní se i hlavní
číslo verze a váš program s novou knihovnou už nebude pracovat. Můžete mít
ale nainstalované různé verze knihoven (starší i novější současně) …
V praxi se ovšem občas stává, chybou programátorů, že se provede v knihovně nějaká nekompatibilní úprava, ale hlavní číslo verze se nezmění. Důsledkem pak je, že váš program začne ze záhadných důvodů (někdy) padat.
Varování
Varování oznamuje že něco není v pořádku, ale překladač si s tím dokáže poradit, nebo to může ignorovat a pokračovat dál v překladu.
Ve zdrojáku volby.c
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 (velké dvojité w). Například volba
-Wformat
bude kontrolovat formátovací řetězce
(u funkcí printf()
, scanf()
,
strftime()
atd.).
volby.c: In function ‘main’:
volby.c:12:2: warning: incompatible implicit declaration of built-in function ‘printf’ [enabled by default]
printf("%s %i\n", slovo, "svete!", x);
^
volby.c:12:2: warning: format ‘%i’ expects argument of type ‘int’, but argument 3 has type ‘char *’ [-Wformat]
volby.c:12:2: warning: too many arguments for format [-Wformat-extra-args]
Ve výstupu vidíte v hranatých závorkách, která volba je zodpovědná za vypsání varování.
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 (nebo g++, ale to je obvykle ta samá manuálová stránka).
Ještě přísnějšího varování dosáhnete při současném použití volby -pedantic.
volby.c:4:6: warning: return type of ‘main’ is not ‘int’ [-Wmain]
volby.c: In function ‘main’:
volby.c:8:5: warning: C++ style comments are not allowed in ISO C90 [enabled by default]
volby.c:8:5: warning: (this will be reported only once per input file) [enabled by default]
volby.c:12:2: warning: implicit declaration of function ‘printf’ [-Wimplicit-function-declaration]
volby.c:12:2: warning: incompatible implicit declaration of built-in function ‘printf’ [enabled by default]
volby.c:12:2: warning: format ‘%i’ expects argument of type ‘int’, but argument 3 has type ‘char *’ [-Wformat]
volby.c:12:2: warning: too many arguments for format [-Wformat-extra-args]
volby.c:6:15: warning: unused variable ‘z’ [-Wunused-variable]
volby.c:10:7: warning: ‘x’ is used uninitialized in this function [-Wuninitialized]
První řádka chybového hlášení říká, že funkce main()
nemá
návratovou hodnotu deklarovanou jako typ int (přestože v Linuxu ji mít musí).
To je tím, že máme v kódu void main(void)
a return;
,
místo int main(void)
a return 0;
.
Další řádek vás informuje, že byly ve funkci main
nalezeny chyby a následuje jejich výpis
(těch chyb nalezených ve funkci main()
).
Třetí řádek si stěžuje na to, že je v kódu použit komentář z c++
(komentář začínající dvěma lomítky). To je opravdu pedantic,
protože v dnešní době najdete stěží překladač jazyka C, který by tento
druh komentářů neznal. Ale -pedantic
požaduje kompatibilitu
se standardem C90, který komentáře s dvěma lomítky ještě nezná.
Na dalším řádku se ještě dočtete, že vás touto
chybou už nebude nadále obtěžovat, pokud na ni narazí na dalších řádcích znova.
Další varování oznamuje, že překladač
nenašel deklaraci funkce printf()
.
To už jsem probral dříve. Napravíte to začleněním
knihovny stdio.h
(kde
je funkce printf()
deklarována) do zdrojového kódu (direktivou #include <stdio.h>
).
Další 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 na char
.
(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 třetí argument ukazatel (pointer) na tento řetězec).
Další varování informuje o tom, že funkce printf()
má více
argumentů, než kolik jich očekává formátovací řetězec.
Předposlední varování oznamuje, že proměnná z nebyla použita (a je tedy v programu zbytečně).
A konečně poslední varování oznamuje, že používáte hodnotu proměnné x, která nebyla inicializována. Ikdyž si můžete vyzkoušet, že taková proměnná obsahuje nulu, ve skutečnosti její hodnota není definována. Takže jednou z tisíce případů se může stát, že alokovaná paměť pro x nebude vynulovaná. A s některými překladači se může stát, že nebude vynulovaná v žádném případě. Neinicializovat proměnnou je mega chyba, která by snad měla být oznamována vždycky, a ne jen s volbou -Wall. (Já ale gcc nepsal, mě si nestěžujte).
Nemusím snad říkat, že byste měli zdrojový kód psát 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. Přesto, raději nejdříve hledejte chybu ve zdrojovém kódu a nesvádějte to na překladač :-).
ANSI
Nesmím opomenout volbu -ansi. Díky této volbě bude překladač kontrolovat, zda zdrojový kód odpovídá normě ANSI, konkrétně verzi C90 pro jazyk C a c++98 pro C++. 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 ukáži, kdy se může tato volba přece jen hodit.
Volba -ansi nevypisuje jen varování, ale občas znemožní i překlad. (Proto asi taky nezačíná na -W.)
volby.c: In function ‘main’:
volby.c:8:5: error: expected expression before ‘/’ token
volby.c:12:2: warning: incompatible implicit declaration of built-in function ‘printf’ [enabled by default]
Jistě víte, že komentáře začínající dvěma lomítky jsou v normě definovány pro jazyk C++, nikoliv C. Proto překladač 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 byla ostuda. K odstranění této a všech dalších potenciálních chyb jsem proto použil volbu -ansi.
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, především kvůli knihovnám třetích stran, které ANSI nerespektují.
Standardy C90 a c++98 jsou navíc už dneska zastaralé, takže volbu
-ansi
nepoužívejte. Místo toho můžete použít
-std=standard
, kde
standard může být (kromě C90 a c++98) C99
pro
ISO standard C99, gnu99
pro GNU rozšíření C99, c++11
pro 2011 ISO C++ standard (který je ale pořád v experimentální verzi, tj.
může se časem měnit) a gnu++11
.
Další možnosti najdete v manuálové stránce.
volby.c: In function ‘main’:
volby.c:12:2: warning: implicit declaration of function ‘printf’
[-Wimplicit-function-declaration]
printf("%s %i\n", slovo, "svete!", x);
^
volby.c:12:2: warning: incompatible implicit declaration of built-in function
‘printf’ [enabled by default]
Standardu C99 už komentář začínající dvěma lomítkami nevadí.
Optimalizace
Překladač je schopný optimalizovat výsledný program jednak co do velikosti programu, tak co do rychlosti. K zapnutí optimalizace se používá volba -O (velké ó), s číslem „síly“ optimalizace (např. -O2). V současné době je možné použít číslo od 0 (bez optimalizace) do 3 (nejlepší optimalizace). Pokud použijete volbu -O bez čísla, jde o zápis ekvivalentní zápisu -O1. Můžete také optimalizovat pouze výslednou velikost: -Os (s jako size).
Optimalizace zpomaluje překlad, takže se doporučuje ji nepoužívat při vývoji, ale až při sestavování finálního produktu.
Hádejte, zda bude mít optimalizace nějaký vliv i při překladu tak malého programu
jako je volby.c
volby.c: In function ‘main’:
volby.c:12:2: warning: incompatible implicit declaration of built-in function ‘printf’ [enabled by default]
$ gcc -o volby2 volby.c -O3
volby.c: In function ‘main’:
volby.c:12:2: warning: incompatible implicit declaration of built-in function ‘printf’ [enabled by default]
$ ls -l volby1 volby2
-rwxr-xr-x 1 petr users 12379 22. čen 23.09 volby1
-rwxr-xr-x 1 petr users 12379 22. čen 23.09 volby2
Jak vidíte, nemá. Ale když jsem psal tuto kapitolu poprvé, tak měl program volby1 velikost 4869 bitů a volby2 jen 4853. Jak hanebně velké dnes výsledné programy jsou! U velkých programů optimalizace ale význam má. Věřte mi :-).
Menší program se rychleji načte do paměti, rychleji
se spustí a je optimalizovaný i na rychlejší běh. Optimalizace jsou dokonce natolik dobré,
že se nedoporučuje psát „optimalizovaný kód“, který se hůře čte. Protože
gcc
dokáže řadu věcí zoptimalizovat za vás. Typickým příkladem jsou lokální proměnné.
Pokud něco uložíte do lokální proměnné a to pak předáte nějaké funkci, nebo použijete
ve výrazu, gcc
za vás sám zkontroluje, zda je opravdu potřeba vyhrazovat
místo pro tuto proměnnou, nebo zda by se nedalo tuto hodnotu rovnou předat tam, kde je potřeba.
Následující dva příklady budou ve výsledku totožné, ačkoliv
ten první je daleko čitelnější!
Optimalziace má ale neblahý důsledek při ladění programů
– výsledný program se neřídí úplně přesně vaším zdrojovým kódem.
Pokud chcete program ladit (překlad s použitím volby -g
),
optimalizaci nepoužívejte.
Tak a tímto bych stručný úvod k překladačům gcc
a
g++
(a clang) ukončil. Ale určitě se v dalších kapitolách
ještě k nějakým volbám dostanu. (Teď nevím, jestli z toho máte radost,
nebo jestli vás to vyděsilo).