gcc 編譯多個源文件


Linux 內核和許多其他自由軟件以及開放源碼應用程序都是用 C 語言編寫並使用 GCC 編譯的。

編譯C++程序

編譯、鏈接命令

-c 只編譯不里鏈接

-o鏈接

例:

g++ file1 -c -o file1.o

g++ file2 -c -o file2.o

g++ file1.o file.o -o exec

生成可執行文件exec

即命令:
 

//(1)分步驟依次編譯、鏈接
 g++   -c   a.cpp   編譯

 g++   -o   a   a.o   生成可執行文件
 //(2) 一次完成編譯鏈接
 也可以   g++   -o   a   a.cpp直接生成可執行文件。

單個源文件

為了進行測試,你可以創建“Hello World”程序:

   #include <stdio.h>

  #include <stdlib.h>

  int main(int argc, char **argv)

  {

      printf(“Hello world!\n”);

      exit(0);

  }

使用如下命令編譯並測試這個代碼:

 # gcc -o hello hello.c

 # ./hello
  Hello wordl!

在默認情況下產生的可執行程序名為a.out,但你通常可以通過 gcc 的“-o”選項來指定自己的可執行程序名稱,如此程序指定可執行文件為hello。

多個源文件

源文件message.c包含一個簡單的消息打印函數: 

    #include <stdio.h>

  void goodbye_world(void)

  {

      printf(“Goodbye, world!\n”);

  }

使用gcc的“-c”標記來編譯支持庫代碼:

 # gcc -c message.c

這一過程的輸出結果是一個名為message.o的文件,它包含適合連接到一個較大程序的已編譯目標代碼。

創建一個簡單的示例程序,它包含一個調用goodbye_world的main函數:

    #include <stdlib.h>

  void goodbye_world(void);

  int main(int argc, char **argv)

  {

      goodbye_world();

      exit(0);

  }

使用GCC編譯這個程序:

 # gcc -c main.c

現在有了兩個目標文件: message.o 和 main.o 。它們包含能夠被 Linux 執行的目標代碼。要從這個目標代碼創建Linux可執行程序,需要再一次調用 GCC 來執行連接階段的工作:

    # gcc -o goodbye message.o main.o

運行編譯結果:

    # ./goodbye
 Goodbye, world!

前面這些單獨的步驟也可以簡化為一個命令,這是因為 GCC 對如何將多個源文件編譯為一個可執行程序有內置的規則。

    # gcc -o goodbye message.c main.c

    # ./goodbye
 Goodbye, world!

使用外部函數庫

GCC 常常與包含標准例程的外部軟件庫結合使用,幾乎每一個 Linux 應用程序都依賴於由 GNU C 函數庫 GLIBC。

應用外部函數庫的例子:

   #include <stdio.h>

  #include <stdlib.h>

  #include <math.h>

  #define MAX_INPUT 25

  int main(int agrc, char **argv)

  {

      char input[MAX_INPUT];

      double angle;

      printf(“Give me an angle (in radians) ==>”);

      if(!fgets(input, MAX_INPUT, stdin)){

      perror(“an error occurred.\n”);

  }

  angle = strtod(input, NULL);

  printf(“sin(%e) = %e\n”, angle, sin(angle));

  return 0;

  }

編譯命令:

    # gcc -o trig -lm trig.c

GCC 的”-lm”選項,它告訴 GCC 查看系統提供的數學庫(libm)。因為Linux和UNIX的系統函數庫通常以”lib”為前綴,所以我們假設它存在。真正的函數庫位置隨系統的不同而不同,但它一般會位於目錄/lib或/usr/lib中,在這些目錄中還有數以百計的其他必需的系統函數庫。

共享函數庫與靜態函數庫

Linux系統上的函數庫分為兩種不同的類型:共享的和靜態的

靜態函數庫

每次當應用程序和靜態連接的函數庫一起編譯時,任何引用的庫函數中的代碼都會被直接包含進最終的二進制程序。

共享函數庫

包含每個庫函數的單一全局版本,它在所有應用程序之間共享。這一過程背后所涉及的機制相當復雜,但主要依靠的是現代計算機的虛擬內存能力,它允許包含庫函數的物理內存安全地在多個獨立用戶程序之間共享。

使用共享函數庫不僅減少了文件的容量和 Linux 應用程序在內存中覆蓋的區域,而且它還提高了系統的安全性。一個被許多不同程序同時調用的共享函數庫很可能會駐留在內存中,以在需要使用它時被立即使用,而不是位於磁盤的交換分區中。這有助於進一步減少一些大型 Linux 應用程序的裝載時間。

將上面的 message.c 作為共享庫函數使用的例子:

 # gcc -fPIC -c message.c

“PIC”命令行標記告訴 GCC 產生的代碼不要包含對函數和變量具體內存位置的引用,這是因為現在還無法知道使用該消息代碼的應用程序會將它連接到哪一段內存地址空間。這樣編譯輸出的文件 message.o 可以被用於建立共享函數庫,我們只需使用gcc的“-shared”標記即可:

    # gcc -shared -o libmessage.so message.o

將上面的mian.c使用共享庫函數ligmessage.so編譯:

    # gcc -o goodbye -lmessage -L. message.o

“-lmessage”標記來告訴 GCC 在連接階段引用共享函數庫 libmessage.so 。“-L.”標記告訴 GCC 函數庫可能位於當前目錄中,否則 GNU 的連接器會查找標准系統函數庫目錄,在本例的情況下,就找不到可用的函數庫了。

此時運行編譯好的goodbye會提示找不到共享函數庫:

    #./goodbye
 ./goodbye: error while loading shared libraries: libmessage.so: cannot open shared object file: No such file or directory

可以使用命令 ldd 來發現一個特定應用程序需要使用的函數庫。ldd搜索標准系統函數庫路徑並顯示一個特定程序使用的函數庫版本。

 #ldd goodbye
  linux-gate.so.1 =>  (0×00493000)

  libmessage.so => not found

  libc.so.6 => /lib/libc.so.6 (0×0097c000)

  /lib/ld-linux.so.2 (0×0095a000)

庫文件 libmessage.so 不能在任何一個標准搜索路徑中找到,而且系統提供的配置文件 /etc/ld.so.conf 也沒有包含一個額外的條目來指定包含該庫文件的目錄。

需要設置一個環境變量LD_LIBRARY_PATH來制定額外的共享函數庫搜索路徑,

   # export LD_LIBRARY_PATH=`pwd`

  # ldd goodbye
   linux-gate.so.1 =>  (0x002ce000)

  libmessage.so => /tmp/cpro/libmessage.so (0x00b0f000)

  libc.so.6 => /lib/libc.so.6 (0x0097c000)

  /lib/ld-linux.so.2 (0x0095a000)

運行程序

    # ./goodbye
 Goodbye, world!

gcc命令常用編譯選項

  1. -c 只預處理、編譯和匯編源程序,不進行連接。編譯器對每一個源程序產生一個目標文件。

  2. -o file 確定輸出文件為file。如果沒有用-o選項,缺省的可執行文件的輸出是 a.out,目標文件和匯編文件的輸出對source.suffix分別是source.o和source.s,預處理的C源程序的輸出是標准輸出stdout。

  3. -Dmacro或-Dmacro=defn 其作用類似於源程序里的#define。例如:% gcc -c -DHAVE_GDBM -DHELP_FILE=\”help\” cdict.c其中第一個- D選項定義宏HAVE_GDBM,在程序里可以用#ifdef去檢查它是否被設置。第二個-D選項將宏HELP_FILE定義為字符串“help”(由於反斜線的作用,引號實際上已成為該宏定義的一部分),這對於控制程序打開哪個文件是很有用的。

  4. -Umacro 某些宏是被編譯程序自動定義的。這些宏通常可以指定在其中進行編譯的計算機系統類型的符號,用戶可以在編譯某程序時加上 -v選項以查看gcc缺省定義了哪些宏。如果用戶想取消其中某個宏定義,用-Umacro選項,這相當於把#undef macro放在要編譯的源文件的開頭。

  5. -Idir 將dir目錄加到搜尋頭文件的目錄列表中去,並優先於在gcc缺省的搜索目錄。在有多個-I選項的情況下,按命令行上-I選項的前后順序搜索。dir可使用相對路徑,如-I../inc等。

  6. -O 對程序編譯進行優化,編譯程序試圖減少被編譯程序的長度和執行時間,但其編譯速度比不做優化慢,而且要求較多的內存。

  7. -O2 允許比-O更好的優化,編譯速度較慢,但結果程序的執行速度較快。

  8. -g 產生一張用於調試和排錯的擴展符號表。-g選項使程序可以用GNU的調試程序GDB進行調試。優化和調試通常不兼容,同時使用-g和-O(-O2)選項經常會使程序產生奇怪的運行結果。所以不要同時使用-g和-O(-O2)選項。

  9. -fpic或-fPIC 產生位置無關的目標代碼,可用於構造共享函數庫。

以上是gcc的編譯選項。gcc的命令行上還可以使用連接選項。事實上,gcc將所有不能識別的選項傳遞給連接程序ld。連接程序ld將幾個目標文件和庫程序組合成一個可執行文件,它要解決對外部變量、外部過程、庫程序等的引用。但我們永遠不必要顯式地調用ld。利用gcc命令去連接各個文件是很簡單的,即使在命令行里沒有列出庫程序,gcc也能保證某些庫程序以正確的次序出現。

gcc命令常用鏈接選項

  1. -Ldir 將dir目錄加到搜尋-l選項指定的函數庫文件的目錄列表中去,並優先於gcc缺省的搜索目錄。在有多個-L選項的情況下,按命令行上-L選項的前后順序搜索。dir可使用相對路徑。如-L../lib等。

  2. -lname 在連接時使用函數庫libname.a,連接程序在-Ldir選項指定的目錄下和/lib,/usr/lib目錄下尋找該庫文件。在沒有使用-static選項時,如果發現共享函數庫libname.so,則使用libname.so進行動態連接。

  3. -static 禁止與共享函數庫連接。

  4. -shared 盡量與共享函數庫連接。

    這是Linux上連接程序的缺省選項。

下面是一個使用gcc進行連接的例子:

% gcc -o prog main.o subr.o -L../lib -lany -lm

原文地址


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM