C/C++編譯器常用參數

  • Linux下常用的是gcc, icc(intel compiler)
  • Windows下則是msvc與mingw

gcc

  • gcc and g++分別是gnu的c & c++編譯器 gcc/g++在執行編譯工作的時候,總共需要4步

    • 預處理,生成.i的文件[預處理器cpp]
    • 將預處理後的文件不轉換成組合語言,生成文件.s[編譯器egcs]
    • 組語變為目標代碼(機器代碼)生成.o的文件[彙編器as]
    • 連接目標代碼,生成可執行程序[鏈接器ld]
  • -c

    • 只激活預處理,編譯,和彙編,也就是他只把程序做成obj文件。
    • gcc -c hello.c 將生成hello.o的obj文件。
  • -S

    • 只激活預處理和編譯,就是指把文件編譯成為彙編代碼。
    • gcc -S hello.c 將生成hello.s的彙編代碼,你可以用文本編輯器察看。
  • -E

    • 只激活預處理,這個不生成文件,你需要把它重定向到一個輸出文件裡面.
    •  gcc -E hello.c > plain.txt
  • -o

    • 制定目標名稱,缺省的時候,gcc 編譯出來的文件是a.out
    • gcc -o hello hello.c
  • -ansi

    • 關閉gnu c中與ansi c不兼容的特性,激活ansi c的專有特性(包括禁止一些asm inline typeof關鍵字,以及UNIX,vax等預處理巨集。
  • -fno-asm
      + 此選項實現ansi選項的功能的一部分,它禁止將asm,inline和typeof用作關鍵字。     

  • -fno-strict-prototype

    • 只對g++起作用,使用這個選項,g++將對不帶參數的函數,都認為是沒有顯式的對參數的個數和類型說明,而不是沒有參數.
    • 而gcc無論是否使用這個參數,都將對沒有帶參數的函數,認為城沒有顯式說明的類型
  • -fthis-is-varialble

    • 向傳統c++看齊,可以使用this當一般變量使用.
  • -include file

    • 含某個代碼,簡單來說,就是便以某個文件,需要另一個文件的時候,就可以用它設定,功能就相當於在代碼中使用#include;
    • gcc hello.c -include /tmp/hello.h
  • -Dmacro

    • 相當於C語言中的#define macro
    • -Dmacro=defn
gcc -c hello.c -o hello.o -DDEBUG 

int main()
{
#ifdef DEBUG
        printf("this is before/n");
#endif
        printf("helloworld/n");
#ifdef DEBUG
        printf("this is end/n");
#endif
        exit(0);
}
  • -Umacro

    • 相當於C語言中的#undef macro
  • -Idir

    • 在你是用#include"file"的時候,gcc/g++會先在當前目錄查找你所制定的頭文件,如果沒有找到,他回到預設的頭文件目錄找
    • 如果使用-I制定了目錄,他回先在你所制定的目錄查找,然後再按常規的順序去找.
    • 對於#include;,gcc/g++會到-I制定的目錄查找,查找不到,然後將到系統的缺省的頭文件目錄查找
  • -I-

    • 就是取消前一個參數的功能,所以一般在-Idir之後使用
  • -idirafter dir

    • 在-I的目錄裡面查找失敗,講到這個目錄裡面查找.
  • -nostdinc

    • 使編譯器不再系統缺省的頭文件目錄裡面找頭文件,一般和-I聯合使用,明確限定頭文件的位置
  • -C

    • 在預處理的時候,不刪除註釋信息,一般和-E使用,有時候分析程序,用這個很方便的.
  • -M, -MM, -MD, -MMD

    • 生成文件關聯的信息。包含目標文件所依賴的所有源代碼你可以用gcc -M hello.c來測試一下,很簡單。
    • MM 和上面的那個一樣,但是它將忽略由#include;造成的依賴關係。
    • -MD與-MMD同上,但將輸出導致.d的檔案中。
  • -llibrary

    • 制定編譯的時候使用的函式庫
    •  gcc -lcurses hello.c , 使用ncurses庫編譯程序.
  • -Ldir

    • 制定編譯的時候,搜索庫的路徑。比如你自己的庫,可以用它制定目錄,不然編譯器將只在標準庫的目錄找。這個dir就是目錄的名稱。
  • -O0, -O1, -O2, -O3

    • 編譯器的優化選項的4個級別,-O0表示沒有優化,-O1為缺省值,-O3優化級別最高  
  • -gstabs, -gstabs+

    • 以stabs格式聲稱調試信息,但是不包括gdb調試信息.
    • 以stabs格式聲稱調試信息,並且包含僅供gdb使用的額外調試信息.
  • -ggdb

    • 此選項將盡可能的生成gdb的可以使用的調試信息.
  • -static

    • 此選項將禁止使用動態庫,所以,編譯出來的東西,一般都很大,也不需要什麼動態連接庫,就可以運行.
  • -share

    • 此選項將盡量使用動態庫,所以生成文件比較小,但是需要系統由動態庫.
  • -traditional

    • 試圖讓編譯器支持傳統的C語言特性
  • -x language filename

    • 設定文件所使用的後綴名,對以後的多個有效。
    • 預設C語言的後綴名稱是.c的,而C++的後綴名是.C或者.cpp
    • 可以使用的參數嗎有下面的這些c, objective-c, c-header, c++, cpp-output, assembler, and assembler-with-cpp.
    • gcc -x c hello.pig
  • -x none filename

    • 關掉上一個選項,也就是讓gcc根據文件名後綴,自動識別文件類型.
    • gcc -x c hello.pig -x none hello2.c
  • -fPIC

    • fPIC 作用於編譯階段,告訴編譯器產生與位置無關程式碼(Position-Independent Code),
    • 則產生的程式碼中,沒有絕對位址,全部使用相對位址,故而程式碼可以被加載器加載到記憶體的任意位址,這正是共用程式庫所要求的,共用程式庫被加載時,在記憶體的位置不是固定的。
    • gcc -shared -fPIC -o 1.so 1.c
    • 如果不加-fPIC,則加載.so檔案的程式碼段時,程式碼段參照的資料物件需要重定位, 重定位會修改程式碼段的內容,這就造成每個使用這個.so檔案程式碼段的程式在核心裡都會生成這個.so檔案程式碼段的copy.每個copy都不一樣,取決於 這個.so檔案程式碼段和資料段記憶體對映的位置.
    • 不加fPIC編譯出來的so,是要再加載時根據加載到的位置再次重定位的.(因為它裡面的程式碼并不是位置無關程式碼)
    • 如果被多個應用程式共同使用,那么它們必須每個程式維護一份so的程式碼副本了.(因為so被每個程式加載的位置都不同,顯然這些重定位后的程式碼也不同,當然無法共用)
    • 我們總是用fPIC來生成so,也從來不用fPIC來生成a.
    • fPIC與動態鏈結可以說基本沒有關係,libc.so一樣可以不用fPIC編譯,只是這樣的so必須要在加載到用戶程式的位址空間時重導所有表目.
    • 因此,不用fPIC編譯so并不總是不好,如果你滿足以下4個需求/條件:

      • 程式庫可能需要經常更新
      • 式庫需要非常高的效率(尤其是有很多全域量的使用時)
      • 式庫并不很大.
      • 式庫基本不需要被多個應用程式共用
    • 用沒有加這個參數的編譯后的共用程式庫,也可以使用的話,可能是兩個原因:

      • gcc預設開啟-fPIC選項
      • loader使你的程式碼位置無關
    • 從GCC來看,shared應該是包含fPIC選項的,但似乎不是所以系統都支援,所以最好顯式加上fPIC選項。

函式庫(library)

  • Library可分成三種,static、shared與dynamically loaded.

Static libraries

  • Static 程式庫用於靜態連結,簡單講是把一堆object檔用ar(archiver)包裝集合起來,檔名以`.a’ 結尾。優點是執行效能通常會比後兩者快,
  • 而且因為是靜態連結,所以不易發生執行時找不到library或版本錯置而無法執行的問題。缺點則是檔案較大,維護度較低;例如library如果發現bug需要更新,那麼就必須重新連結執行檔。
    • 編譯方式很簡單,先用`-c’ 編出object 檔,再用ar 包起來即可。
    • gcc -c hello.c world.c / 編出hello.o 與world.o /
    • ar rcs libmylib.a hello.o world.o / 包成limylib.a /
    • 這樣就可以建出一個檔名為libmylib.a 的檔。輸出的檔名其實沒有硬性規定,但如果想要配合gcc 的’-l’ 參數來連結,一定要以lib’ 開頭,中間是你要的library名稱,然後緊接著.a’ 結尾。
#include “mylib.h”
int main() {
    hello();
    world();
}
gcc main.c libmylib.a或gcc main.c -L. -lmylib

shared libraries

  • Shared library 會在程式執行起始時才被自動載入。因為程式庫與執行檔是分離的,所以維護彈性較好。有兩點要注意,shared library是在程式起始時就要被載入,而不是執行中用到才載入,而且在連結階段需要有該程式庫才能進行連結。

  • 首先有一些名詞要弄懂,soname、real name與linker name。

    • soname 用來表示是一個特定library 的名稱,像是libmylib.so.1 。前面以lib’ 開頭,接著是該library 的名稱,然後是.so’ ,接著是版號,用來表名他的介面;如果介面改變時,就會增加版號來維護相容度。
    • real name 是實際放有library程式的檔案名稱,後面會再加上minor 版號與release 版號,像是libmylib.so.1.0.0 。
    • 一般來說,版號的改變規則是尾碼的release版號用於程式內容的修正,介面完全沒有改變。中間的minor用於有新增加介面,但相舊介面沒改變,所以與舊版本相容。最前面的version版號用於原介面有移除或改變,與舊版不相容時。
    • linker name是用於連結時的名稱,是不含版號的soname ,如: libmylib.so。通常linker name與real name是用ln 指到對應的real name ,用來提供彈性與維護性。
  • shared library的製作過程較複雜:

    • gcc -c -fPIC hello.c world.c
      • 編譯時要加上-fPIC 用來產生position-independent code。
    • gcc -shared -Wl,-soname,libmylib.so.1 -o libmylib.so.1.0.0 hello.o world.o
      • shared 表示要編譯成shared library
      • Wl 用於參遞參數給linker,因此-soname與libmylib.so.1會被傳給linker處理。
      • soname用來指名soname 為limylib.so.1
      • library會被輸出成libmylib.so.1.0.0 (也就是real name)
      • 若不指定soname 的話,在編譯結連後的執行檔會以連時的library檔名為soname,並載入他。否則是載入soname指定的library檔案。
    • 可以利用objdump 來看library 的soname。objdump -p libmylib.so
    • 在編譯後再用ln 來建立soname 與linker name 兩個檔案。
      • ln -s libmylib.so.1.0.0 libmylib.so
      • ln -s libmylib.so.1.0.0 libmylib.so.1
  • 使用時與static library 相同

    • gcc main.c libmylib.so 直接指定與libmylib.so 連結。
    • 或者用 gcc main.c -L. -lmylib inker會搜尋libmylib.so 來進行連結
  • 如果目錄下同時有static與shared library的話,會以shared為主。

    • 使用-static 參數可以避免使用shared連結。
    • gcc main.c -static -L. -lmylib
  • 可以用ldd 看編譯出的執行檔與shared程式庫的相依性

    • ldd a.out

找不到shared library的處理方式

  • 假設找不到ibmylib.so.1
    • 把libmylib.so.1 安裝到系統的library目錄,如/usr/lib下。
    • 設定/etc/ld.so.conf ,加入一個新的library搜尋目錄,並執行ldconfig更新快取
    • 設定LD_LIBRARY_PATH 環境變數來搜尋library, LD_LIBRARY_PATH=. ./a.out

Dynamically loaded libraries

  • Dynamicaaly loaded libraries 才是像windows 所用的DLL ,在使用到時才載入,編譯連結時不需要相關的library。動態載入庫常被用於像plugins的應用。
  • 動態載入是透過一套dl function來處理。
    • include

    • void dlopen(const char filename, int flag); 開啟載入filename 指定的library。
    • void dlsym(void handle, const char *symbol); 取得symbol 指定的symbol name在library被載入的記憶體位址。
    • int dlclose(void *handle);關閉dlopen開啟的handle。
    • char *dlerror(void); 傳回最近所發生的錯誤訊息。
// dltest.c
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

int main() {
    void *handle;
    void (*f)();
    char *error;

    /* 開啟之前所撰寫的libmylib.so 程式庫*/
    handle = dlopen(“./libmylib.so”, RTLD_LAZY);

    if( !handle ) {
        fputs( dlerror(), stderr);
        exit(1);
    }

    /* 取得hello function 的address */
    f = dlsym(handle, “hello”);
    if(( error=dlerror())!=NULL) {
        fputs(error, stderr);
        exit(1);
    }

    /* 呼叫該function */
    f();
    dlclose(handle);
}
  • 編譯時要加上-ldl 參數來與dl library 連結: gcc dltest.c -ldl

results matching ""

    No results matching ""