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.

VolbaVýznam
-vVypíše verzi překladače a volby, se kterými byl přeložen.
-o programUrčuje název výsledného přeloženého programu.
-WallZobrazování všech varování (warning all).
-ansiKontrola standardu ANSI c90. Zakazuje některá GNU/Linux rozšíření, takže to v tomto kurzu určitě nepoužívejte :-)
-pedanticPřísnější konstrola standardu. (To taky nepoužívejte.)
-O2Optimalizace 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++
-lknihovnaPř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++
-LdirPřidá adresář dir do cest, které prohledává volba -l
-gDo 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ý.

$ gcc -v
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.

  1. /*------------------------------------------------*/
  2. /* 03volby/volby.c                                */
  3.  
  4. void main(void)
  5. {
  6.     int x, y, z;
  7.     char slovo[] = "Ahoj ";
  8.     // x nebylo inicializovano
  9.  
  10.     y = x;
  11.     for (; y > x + 1; y++)
  12.         printf("%s %i\n", slovo, "svete!", x);
  13.     return;
  14. }
  15. /*------------------------------------------------*/

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:

  1. $ gcc -o volby volby.c
  2. volby.c: In function ‘main’:
  3. volby.c:12:2: warning: incompatible implicit declaration of built-in functionprintf[enabled by default]
  4.   printf("%s %i\n", slovo, "svete!", x);
  5.   ^

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

Chyba je způsobena tím, že ve zdrojovém souboru chybí #include <stdio.h>, kde je funkce printf() deklarována.

A teď s volbou -Wall:

$ gcc -o volby volby.c -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 functionprintf[-Wimplicit-function-declaration]
  printf("%s %i\n", slovo, "svete!", x);
  ^
volby.c:12:2: warning: incompatible implicit declaration of built-in functionprintf[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.

$ clang -o volby volby.c -Wall
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.

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.

Knihovny funkcí můžete najít například v /lib/ nebo /usr/lib64/ atp. Distribuce od distribuce se to může lišit. Já mám třeba matematickou knihovnu uloženou v souboru /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).

  1. /*------------------------------------------------*/
  2. /* 03volby/math.c                                */
  3.  
  4. #include <stdio.h>
  5. #include <math.h>
  6.  
  7. int main(void)
  8. {
  9.     int a = 3, b = 4;
  10.     float c;
  11.  
  12.     double ea = pow(a, 2);
  13.     double eb = pow(b, 2);
  14.     c = sqrt(ea + eb);
  15.  
  16.     printf("c = sqrt(%.0lf + %.0lf) = %f\n", ea, eb, c);
  17.  
  18.     return 0;
  19. }
  20. /*------------------------------------------------*/

Nejdříve pokus bez přilinkování knihovny /lib/libm.so.6.

$ gcc -o math math.c
/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.

$ gcc -o math math.c -lm
$ ./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.).

$ gcc -o volby volby.c -Wformat
volby.c: In function ‘main’:
volby.c:12:2: warning: incompatible implicit declaration of built-in functionprintf[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.

$ gcc -o volby -Wall -pedantic volby.c
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 functionprintf[-Wimplicit-function-declaration]
volby.c:12:2: warning: incompatible implicit declaration of built-in functionprintf[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;.

Když jsem psal tuto kapitolu kdysi poprvé, toto varování bylo "default", takže se zobrazovalo i bez volby -Wall, resp. -Wmain. Naopak se nezobrazovalo varování incompatible implicit declaration of built-in function ‘printf’, které je teď default. Takže dnes, v roce 2053, už zase může být všechno jinak …

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

$ gcc -ansi volby.c -o volby
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 functionprintf[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.

$ gcc -std=c99 volby.c -o volby
volby.c: In function ‘main’:
volby.c:12:2: warning: implicit declaration of functionprintf
[-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

$ gcc -o volby1 volby.c
volby.c: In function ‘main’:
volby.c:12:2: warning: incompatible implicit declaration of built-in functionprintf[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 functionprintf[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ší!

int x;
x = getSomeNumer();
printf("x = %i\n", x);
printf("x = %i\n", getSomeNumer());

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

Komentář Hlášení chyby
Created: 11.9.2005
Last updated: 14.11.2015
Tato stánka používá ke svému běhu cookies, díky kterým je možné monitorovat, co tu provádíte (ne že bych to bez cookies nezvládl). Také vás tu bude špehovat google analytics. Jestli si myslíte, že je to problém, vypněte si cookies ve vašem prohlížeči, nebo odejděte a už se nevracejte :-). Prohlížením tohoto webu souhlasíte s používáním cookies. Dozvědět se více..