為了防止無良網站的爬蟲抓取文章,特此標識,轉載請註明文章出處。LaplaceDemon/ShiJiaqi。

http://www.cnblogs.com/shijiaqi1066/p/6065410.html

gcc命令

gcc 選項 引數

選項:

  • -o:指定生成的輸出檔案的檔名;
  • -E:僅執行編譯預處理;
  • -S:將C程式碼轉換為彙編程式碼;
  • -wall:顯示警告資訊;
  • -c:僅執行編譯操作,不進行連線操作。
  • -D FOO=X:在命令列定義預處理巨集FOO,其值為X。
  • -I dir:新增標頭檔案(.h)搜尋路徑
  • -L dir:新增庫檔案搜尋路徑。
  • -static:指定連結庫為連結靜態庫。
  • -l, -library:指定連結庫檔名。
  • -g:在可執行程式中包含標準除錯資訊。
  • -ggdb:產生除錯資訊,僅供gnu識別。
  • -O 數字:指定程式碼優化的級別為N,0<=N<=3。 該選項不能與-g選項聯合使用;
  • -ansi:支援ANSI/ISO C的標準語法。
  • -pedantic:允許發出ANSI/ISO C標準所列出的所有警告。
  • -pedantic -errors:允許發出ANSI/ISO C標準所列出的所有錯誤。
  • -traditional:支援K&R C語法。
  • -w:關閉所有警告。
  • -Wall:允許發出gcc能提供的所有有用的警告。
  • -werror:把所有警告轉換為錯誤,在警告發生時中止編譯過程。
  • -shared:編譯動態連結庫(共享函式庫)。

引數:C程式碼的原始檔。

現在有一個簡單的main可執行程式。

#include <stdio.h>

int main(void) {
    printf("Hello World!\n");
    ;
}

簡單編譯

1. 無選項編譯

gcc test.c

經過該命令,程式碼被編譯成a.out可執行程式。

[root@localhost HelloWorld]# ./a.out
hello world!!!

2. 指定輸出檔名

編譯命令:

gcc test.c -o test

將程式碼編譯可執行檔案test。-o選項用來指定輸出檔案的檔名。

[root@localhost HelloWorld]# ./test
hello world!!!

編譯的四個階段

在使用gcc編譯命令之前,一定要先了解程式的編譯過程。

整個編譯被分為四個過程:

  • 預處理(也稱預編譯,Pre-Processing)
  • 編譯(Compilation)
  • 彙編 (Assembly)
  • 連線(Linking)

預處理階段:執行預處理指令,原始檔會被修改。如#include指令就是一個預處理指令,它把標頭檔案的內容新增到 .c/.cpp 檔案中。該階段檔案從.c 處理成 .i。但本質還是C程式碼。

編譯階段:該階段會把C程式碼翻譯成等價的中間程式碼或彙編程式碼,並優化。

彙編階段:該階段把彙編程式碼翻譯成目標機器指令。

連結階段:該階段把相關聯的目標檔案相互連線。也就是將在一個檔案中引用的符號同該符號在另外一個檔案中的定義連線起來,使得所有的這些目標檔案成為一個可執行的統一整體。

連結被分為兩種:

靜態連結:

在這種連結方式下,函式的程式碼將從其所在地靜態連結庫中被拷貝到最終的可執行程式中。這樣該程式在被執行時這些程式碼將被裝入到該程序的虛擬地址空間中。靜態連結庫實際上是一個目標檔案的集合,其中的每個檔案含有庫中的一個或者一組相關函式的程式碼。

動態連結:

多個程式可以公用一份函式記憶體。程式在編譯中不依賴外部函式庫,只在執行時尋找相應庫,動態連結,執行。

相應的,這裡涉及到一個重要概念即庫函式。

庫函式也被分為靜態庫與動態庫。

靜態庫是把庫檔案的程式碼全部加入到可執行檔案中,因此生成的檔案比較大,但在執行時也就不需要庫檔案了。靜態庫的字尾一般為“.a”。

動態庫與之相反,在編譯連結時並沒有把庫檔案的程式碼加入到可執行檔案中,而是在程式執行時連結檔案載入庫。動態庫的字尾一般為“.so”。

例如,stdio.h中的printf,其具體實現位於動態庫libc.so.6。在沒有特別指定時,gcc命令會去/usr/lib下查詢。也就是連結到libc.so.6庫函式。

詳解gcc命令

1. 預處理

gcc -E test.c -o test.i

可以開啟test.i檔案,該檔案依然是C程式碼,只是加入了很多其他程式碼。

2. 編譯

gcc -S test.i -o test.s

可以開啟test.s檔案,檢視彙編程式碼,注意,這依然是文字檔案。

        .file   "test.c"
        .section        .rodata
.LC0:
        .string "hello world!!!"
        .text
        .globl  main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset
        .cfi_offset , -
        movq    %rsp, %rbp
        .cfi_def_cfa_register
        movl    $.LC0, %edi
        call    puts
        movl    $, %eax
        popq    %rbp
        .cfi_def_cfa ,
        ret
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-4)"
        .section        .note.GNU-stack,"",@progbits

3. 彙編

例:

gcc -c test.s -o test.o 或 gcc -c test.s

-c選項,將C程式碼或彙編程式碼編譯成二進位制檔案。該過程會自動包含預處理,編譯,彙編,但不執行連結過程。若原始檔名為abc.c則預設輸出為abc.o,檔名不變。

這裡的test.o就無法用vim或者其他文字程式打開了,因為這已經是二進位制程式了。

4. 連結

gcc test.o -o test

經過連結後,程式碼即可執行。

[root@localhost HelloWorld]# ./test
hello world!!!

5. 多個程式檔案編譯

一般程式都是以多個原始檔的形式組成的。需要把多個檔案編譯成二進位制檔案,然後將編譯多個檔案然後連結在一起。

例:原始檔為test1.c與test2.c。

可以使用:

gcc test1.c test2.c -o test

該語句本質上由如下語句組成:

gcc -c test1.c -o test1.o

gcc -c test1.c -o test2.o

gcc test1.o test2.o -o test

6.警告檢查

-pedantic  以ANSI/ISO C標準列出的所有警告。

-Wall        顯示所有的警告資訊。

-Werror    要求GCC將所有的警告當成錯誤進行處理。

7. 定義巨集

#include <stdio.h>

int main(void){
#ifdef HELLO
    printf("HELLO defined!,HELLO = %d\n",HELLO);
#else
    printf("HELLO is not define!\n");
#endif
    ;
}

使用編譯選項 -D 來定義巨集的值。

gcc -DHELLO=100 hello.c -o hello

巨集被定義為100。

[root@localhost HelloWorld]# ./hello
HELLO defined!,HELLO = 100

gcc hello.c -o hello

不定義巨集的值。
[root@localhost HelloWorld]# ./hello
HELLO is not define!

8. 連結庫檔案

動態庫和靜態庫的使用方法是一樣的,同一個庫如果同時存在動態庫和靜態庫,優先連結動態庫,

gcc中關於庫的引數有:
-L:指定搜尋庫的目錄。如指定當前目錄 gcc -L 。
 
-l(小寫L):指定要連結的庫的名稱。
例:加入庫的名稱是libmylib.a。則可以使用 gcc -l mylib,即去頭去尾。
 
--static    組織在連結時使用動態庫。該選項用於當同時存在動態庫與靜態庫時,強制使用靜態庫。
--shared   生成動態庫。
--static-libgcc  連結靜態libgcc庫
--shared-libgcc 連結動態libgcc庫

9. 編譯生成靜態庫

靜態庫即.a檔案。靜態庫的名字一般為libxxxx.a,其中xxxx是該lib的名稱。

靜態函式庫實際上就是簡單的一個普通的目標檔案的集合。.a檔案就是是.o檔案的集合。

使用ar命令就可以讓一組.o檔案生成.a檔案。Ar是archiver的縮寫。

格式:ar rcs my_library.a file1.o file2.o

例:已經存在a.o, b.o, c.o,組合生成 libmylib.a 。

ar rcs libmylib.a a.o b.o c.o 

使用ar命令對靜態庫新增.o

ar -r libhello.a new_hello.o

10. 編譯生成動態庫

動態庫即.so檔案。動態庫的名字一般為libxxxx.so.major.minor,xxxx是該lib的名稱,major是主版本號, minor是副版本號

動態庫必須由gcc編譯生成。

例:a.c, b.c兩個檔案,生成 libmylib.so 。

gcc --shared a.c b.c -fPIC -o libmylib.so

PIC的意思是“位置無關程式碼”(Position Independent Code)。

安裝動態連結庫(共享函式庫)

方法一:其實簡單的方法就是拷貝so檔案到指定的標準的目錄(如,/usr/lib),然後執行ldconfig。

若不執行ldconfig會引發 "error while loading shared libraries...."異常,無法執行。

方法二:使用ldconfig動態新增so檔案位置。

例:ldconfig /usr/zhsoft/lib

方法三:若沒有許可權去做這件事情,例如你不能修改/usr/lib目錄,可以通過修改環境變數來實現使用函式庫。

將so所在目錄匯入到環境變數LD_LIBRARY_PATH中,即可。

export LD_LIBRARY_PATH=$(pwd)

接著即可執行,不需要使用ldconfig命令。

11. 檢視可執行程式和動態連結庫的依賴

例:檢視動態連結庫的依賴。

[root@localhost hello]# ldd ./libhello.so
linux-vdso.so. => (0x00007fffd57fe000)
libc.so. => /lib64/libc.so. (0x00007fdf76ac9000)
/lib64/.so. (0x00007fdf77094000)

例:檢視ls命令的依賴。

[root@localhost hello]# ldd /usr/bin/ls
linux-vdso.so. => (0x00007ffc85712000)
libselinux.so. => /lib64/libselinux.so. (0x00007f27f4262000)
libcap.so. => /lib64/libcap.so. (0x00007f27f405d000)
libacl.so. => /lib64/libacl.so. (0x00007f27f3e53000)
libc.so. => /lib64/libc.so. (0x00007f27f3a91000)
libpcre.so. => /lib64/libpcre.so. (0x00007f27f3830000)
liblzma.so. => /lib64/liblzma.so. (0x00007f27f360a000)
libdl.so. => /lib64/libdl.so. (0x00007f27f3406000)
/lib64/.so. (0x00007f27f448e000)
libattr.so. => /lib64/libattr.so. (0x00007f27f3201000)
libpthread.so. => /lib64/libpthread.so. (0x00007f27f2fe4000)

12. 程式碼優化 

-O 數字,數字指定程式碼優化的級別為,級別分0,1,2,3 。

-O1 告訴編譯器進行第一級優化,通常提高優化級別會使得程式執行的更快,但是編譯的時間會變長,用除錯工具除錯程式變得更加困難,使用更高的級別優化程式碼,使得產生的機器程式碼難以理解。

編譯實戰

1. 原始碼

hello.h

#ifndef HELLO_H
#define HELLO_H 

void hello(const char *name); 

#endif

hello.c

# include <stdio.h>
void hello(const char *name) {
    printf("Hello %s!\n", name);
}

main.c

# include "hello.h"
int main() {
     hello("everyone");
     ;
 }

2. 手動多檔案編譯

gcc hello.c main.c -o hello

gcc -c hello.c
gcc -c main.c
gcc hello.o main.o -o hello

3. 生成並使用靜態連結庫

使用ar命令生成.a檔案。

gcc -c hello.c    #生成hello.o
ar rcs libhello.a hello.o    #生成libhello.a
gcc main.c -L. -lhello -o hello    # -L. 表示連結庫目錄為當前目錄,-l表示連結庫名稱為libhello

執行

./hello

4. 生成並使用動態連結庫

方法一:

#生成so檔案
gcc -shared hello.c -fPIC -o libhello.so

#安裝
mv libhello.so /usr/lib
ldconfig

#執行
./hello

方法二:

#安裝
ldconfig /root/workspace-c/GCC/hello/build

#執行
./hello

方法三:

#安裝
export LD_LIBRARY_PATH=$(pwd)

#執行
./hello

編譯中一些檔案路徑的注意點

  1. gcc命令使用-l選項指定連結庫的名稱。-L選項指定連結庫所在目錄。
  2. .h 檔案是編譯過程必不可少的。.h 檔案需要在連結階段被使用。.h 檔案路徑不需要與相關的 .a 或 .o 在一起。事實上,該檔案是由#include時候指定目錄的
  3. 使用-I引數可以新增.h檔案搜尋的路徑。

靜態庫連結時搜尋路徑順序:

  1. ld會去找GCC命令中的引數 -L
  2. 再找gcc的環境變數LIBRARY_PATH
  3. 再找內定目錄 /lib /usr/lib /usr/local/lib 這是當初compile gcc時寫在程式內的

動態連結時、執行時搜尋路徑順序:

  1. 編譯目的碼時指定的動態庫搜尋路徑;
  2. 環境變數LD_LIBRARY_PATH指定的動態庫搜尋路徑;
  3. 配置檔案/etc/ld.so.conf中指定的動態庫搜尋路徑;
  4. 預設的動態庫搜尋路徑/lib;
  5. 預設的動態庫搜尋路徑/usr/lib。

有關環境變數:

  • LIBRARY_PATH環境變數:指定程式靜態連結庫檔案搜尋路徑。
  • LD_LIBRARY_PATH環境變數:指定程式動態連結庫檔案搜尋路徑。

程式碼中動態使用動態連結庫(無需安裝)

使用ldopen函式,略。

為了防止無良網站的爬蟲抓取文章,特此標識,轉載請註明文章出處。LaplaceDemon/ShiJiaqi。

http://www.cnblogs.com/shijiaqi1066/p/6065410.html