Linux的編譯器gcc 和g++
GNU程序編譯
命令:gcc
g++
格式:gcc [option] filename
g++ [option] filename
功能:編譯或鏈接指定的編譯文件。
選項:-o 指定輸出文件名,缺省時爲a.out
-c 只編譯,產生.o的目標文件
-O 進行代碼的一般優化
-O2 二級優化
一.C語言程序的編譯——gcc用法的詳細說明
摘自:OwnLinux.cn
GNU 編譯器集(其前身爲GNU C 編譯器)誕生於1987年。當時Richard Stallman(GNU 項目的創辦人)想要創建一個編譯器,它可以滿足他定義的“自由軟件”概念,並可用來編譯 GNU 項目發佈的其他軟件。GNU C 編譯器迅速在自由軟件社區中流行開來,而且以其健壯性和可移植性而聞名。它已成爲許多集成開發工具的基礎,被世界各地的發行商應用在 Linux 和其他操作系統之上。
GCC 已不再是主要針對GNU項目自身的軟件的小型 C 語言編譯器了。如今,它已支持了許多不同的語言,包括 C、C++、Ada、Fortran、Objective C,甚至還有Java。事實上,現代 Linux 系統除了可以自豪地炫耀那些由 GNU 工具直接支持的語言以外,它還支持大量其他語言。日益流行的腳本語言 Perl、Python 和 Ruby,以及正在不斷髮展的mono 可移植C#實現的確有助於沖淡人們對 Linux 編程的傳統看法,但這完全是另外一個問題了。
Linux 內核和許多其他自由軟件以及開放源碼應用程序都是用 C 語言編寫並使用 GCC 編譯的。
1. 編譯單個源文件
爲了進行測試,你可以創建“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”選項來指定自己的可執行程序名稱。
2. 編譯多個源文件
源文件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!
二、C++程序的編譯—— g++用法的詳細說明
C++編譯
單個源文件生成可執行程序
下面是一個保存在文件 helloworld.cpp 中一個簡單的 C++ 程序的代碼:
/* helloworld.cpp */
#include <iostream>
int main(int argc,char *argv[])
{
std::cout << "hello, world" << std::endl;
return(0);
}
程序使用定義在頭文件 iostream 中的 cout,向標準輸出寫入一個簡單的字符串。該代碼可用以下命令編譯爲可執行文件:
$ g++ helloworld.cpp
編譯器 g++ 通過檢查命令行中指定的文件的後綴名可識別其爲 C++ 源代碼文件。編譯器默認的動作:編譯源代碼文件生成對象文件(object file),鏈接對象文件和 libstdc++ 庫中的函數得到可執行程序。然後刪除對象文件。由於命令行中未指定可執行程序的文件名,編譯器採用默認的 a.out。程序可以這樣來運行:
$ ./a.out
hello, world
更普遍的做法是通過 -o 選項指定可執行程序的文件名。下面的命令將產生名爲 helloworld 的可執行文件:
$ g++ helloworld.cpp -o helloworld
在命令行中輸入程序名可使之運行:
$ ./helloworld
hello, world
程序 g++ 是將 gcc 默認語言設爲 C++ 的一個特殊的版本,鏈接時它自動使用 C++ 標準庫而不用 C 標準庫。通過遵循源碼的命名規範並指定對應庫的名字,用 gcc 來編譯鏈接 C++ 程序是可行的,如下例所示:
$ gcc helloworld.cpp -lstdc++ -o helloworld
選項 -l (ell) 通過添加前綴 lib 和後綴 .a 將跟隨它的名字變換爲庫的名字 libstdc++.a。而後它在標準庫路徑中查找該庫。gcc 的編譯過程和輸出文件與 g++ 是完全相同的。
在大多數系統中,GCC 安裝時會安裝一名爲 c++ 的程序。如果被安裝,它和 g++ 是等同,如下例所示,用法也一致:
$ c++ helloworld.cpp -o helloworld
多個源文件生成可執行程序
如果多於一個的源碼文件在 g++ 命令中指定,它們都將被編譯並被鏈接成一個單一的可執行文件。下面是一個名爲 speak.h 的頭文件;它包含一個僅含有一個函數的類的定義:
/* speak.h */
#include <iostream>
class Speak
{
public:
void sayHello(const char *);
};
下面列出的是文件 speak.cpp 的內容:包含 sayHello() 函數的函數體:
/* speak.cpp */
#include "speak.h"
void Speak::sayHello(const char *str)
{
std::cout << "Hello " << str << "/n";
}
文件 hellospeak.cpp 內是一個使用 Speak 類的程序:
/* hellospeak.cpp */
#include "speak.h"
int main(int argc,char *argv[])
{
Speak speak;
speak.sayHello("world");
return(0);
}
下面這條命令將上述兩個源碼文件編譯鏈接成一個單一的可執行程序:
$ g++ hellospeak.cpp speak.cpp -o hellospeak
PS:這裏說一下爲什麼在命令中沒有提到“speak.h“該文件(原因是:在“speak.cpp“中包含有”#include"speak.h"“這句代碼,它的意思是搜索系統頭文件目錄之前將先在當前目錄中搜索文件“speak.h“。而”speak.h“正在該目錄中,不用再在命令中指定了)。
源文件生成對象文件
選項 -c 用來告訴編譯器編譯源代碼但不要執行鏈接,輸出結果爲對象文件。文件默認名與源碼文件名相同,只是將其後綴變爲 .o。例如,下面的命令將編譯源碼文件 hellospeak.cpp 並生成對象文件 hellospeak.o:
$ g++ -c hellospeak.cpp
命令 g++ 也能識別 .o 文件並將其作爲輸入文件傳遞給鏈接器。下列命令將編譯源碼文件爲對象文件並將其鏈接成單一的可執行程序:
$ g++ -c hellospeak.cpp
$ g++ -c speak.cpp
$ g++ hellospeak.o speak.o -o hellospeak
選項 -o 不僅僅能用來命名可執行文件。它也用來命名編譯器輸出的其他文件。例如:除了中間的對象文件有不同的名字外,下列命令生將生成和上面完全相同的可執行文件:
$ g++ -c hellospeak.cpp -o hspk1.o
$ g++ -c speak.cpp -o hspk2.o
$ g++ hspk1.o hspk2.o -o hellospeak
編譯預處理
選項 -E 使 g++ 將源代碼用編譯預處理器處理後不再執行其他動作。下面的命令預處理源碼文件 helloworld.cpp 並將結果顯示在標準輸出中:
$ g++ -E helloworld.cpp
本文前面所列出的 helloworld.cpp 的源代碼,僅僅有六行,而且該程序除了顯示一行文字外什麼都不做,但是,預處理後的版本將超過 1200 行。這主要是因爲頭文件 iostream 被包含進來,而且它又包含了其他的頭文件,除此之外,還有若干個處理輸入和輸出的類的定義。
預處理過的文件的 GCC 後綴爲 .ii,它可以通過 -o 選項來生成,例如:
$ gcc -E helloworld.cpp -o helloworld.ii
生成彙編代碼
選項 -S 指示編譯器將程序編譯成彙編語言,輸出彙編語言代碼而後結束。下面的命令將由 C++ 源碼文件生成彙編語言文件 helloworld.s:
$ g++ -S helloworld.cpp
生成的彙編語言依賴於編譯器的目標平臺。
創建靜態庫
靜態庫是編譯器生成的一系列對象文件的集合。鏈接一個程序時用庫中的對象文件還是目錄中的對象文件都是一樣的。庫中的成員包括普通函數,類定義,類的對象實例等等。靜態庫的另一個名字叫歸檔文件(archive),管理這種歸檔文件的工具叫 ar 。
在下面的例子中,我們先創建兩個對象模塊,然後用其生成靜態庫。
頭文件 say.h 包含函數 sayHello() 的原型和類 Say 的定義:
/* say.h */
#include <iostream>
void sayhello(void);
class Say {
private:
char *string;
public:
Say(char *str)
{
string = str;
}
void sayThis(const char *str)
{
std::cout << str << " from a static library/n";
}
void sayString(void);
};
下面是文件 say.cpp 是我們要加入到靜態庫中的兩個對象文件之一的源碼。它包含 Say 類中 sayString() 函數的定義體;類 Say 的一個實例 librarysay 的聲明也包含在內:
/* say.cpp */
#include "say.h"
void Say::sayString()
{
std::cout << string << "/n";
}
Say librarysay("Library instance of Say");源碼文件 syshello.cpp 是我們要加入到靜態庫中的第二個對象文件的源碼。它包含函數 sayhello() 的定義:
/* sayhello.cpp */
#include "say.h"
void sayhello()
{
std::cout << "hello from a static library/n";
}
下面的命令序列將源碼文件編譯成對象文件,命令 ar 將其存進庫中:
$ g++ -c sayhello.cpp
$ g++ -c say.cpp
$ ar -r libsay.a sayhello.o say.o
程序 ar 配合參數 -r 創建一個新庫 libsay.a 並將命令行中列出的對象文件插入。採用這種方法,如果庫不存在的話,參數 -r 將創建一個新的庫,而如果庫存在的話,將用新的模塊替換原來的模塊。
下面是主程序 saymain.cpp,它調用庫 libsay.a 中的代碼:
/* saymain.cpp */
#include "say.h"
int main(int argc,char *argv[])
{
extern Say librarysay;
Say localsay = Say("Local instance of Say");
sayhello();
librarysay.sayThis("howdy");
librarysay.sayString();
localsay.sayString();
return(0);
}
該程序可以下面的命令來編譯和鏈接:
$ g++ saymain.cpp libsay.a -o saymain
程序運行時,產生以下輸出:
hello from a static library
howdy from a static library
Library instance of SayLocal instance of Say