本文討論GNU編譯器集合(GCC)中的C++編譯器(g++)的典型用法,主要是指命令行選項(xiàng)的構(gòu)造。GCC的C++編譯器正常安裝后,可以使用g++或c++命令執(zhí)行。
GCC Option Refresher
本節(jié)回顧GCC的C編譯器的基本使用方法。
g++編譯器的選項(xiàng)可以是單字符,比如-o,也可以多字符,比如-ansi。所以你不可以把多個(gè)單字符選項(xiàng)合寫(xiě)到一起,這和許多其他GNU和UNIX下的程序不同。例如,多字符選項(xiàng)-pg不表示2個(gè)單字符選項(xiàng)-p -g。選項(xiàng)-pg表示在最終的2進(jìn)制文件里生成額外的代碼,用來(lái)輸出GNU code profiler的信息gprof;而選項(xiàng)-p -g則表示在目標(biāo)2進(jìn)制文件里生成額外的代碼,用來(lái)產(chǎn)生prof code profiler需要的信息(-p),并在目標(biāo)里加入調(diào)試信息(-g)。
既然g++把多字符的選項(xiàng)進(jìn)行了區(qū)分,你就可以隨意安排各個(gè)選項(xiàng)的順序了。比如:
g++ -pg -fno-strength-reduce -g myprog.c -o myprog
和
g++ myprog.c -o myprog -g -fno-strength-reduce -pg
是一樣的。
一般情況下,這些選項(xiàng)的順序是無(wú)所謂的。但是在有些情況下,順序會(huì)變得重要,比如你多次使用同一類的選項(xiàng)。舉個(gè)例子,-I選項(xiàng)指定了搜索include文件的目錄,如果你用-I指定了多個(gè)目錄,gcc會(huì)按照你指定目錄的順序搜索需要的文件。
用g++編譯單個(gè)源文件myprog.cc很簡(jiǎn)單,只要把文件名當(dāng)參數(shù)傳給g++就行了。
$ g++ myprog.cc
$ ls -l -rwxr-xr-x 1 wvh users 13644 Oct 5 16:17 a.out
-rw-r--r-- 1 wvh users 220 Oct 5 16:17 myprog.cc 默認(rèn)情況下,UNIX和LINUX操作系統(tǒng)生成的目標(biāo)文件是當(dāng)前目錄下的a.out,只要輸入./a.out就可以執(zhí)行。在Cygwin系統(tǒng)下,你得到的是a.exe,通過(guò)輸入./a或者./a.exe都可以執(zhí)行。
要定義g++的輸出文件名,使用-o選項(xiàng)如下所示:
$ g++ myprog.cc -o runme
$ ls -l
-rw-r--r-- 1 wvh users 220 Oct 5 16:17 myprog.cc
-rwxr-xr-x 1 wvh users 13644 Oct 5 16:28 runme 如果編譯多個(gè)源文件,只要在命令行里列出它們就行了,如下所示,最終產(chǎn)生的輸出文件是showdate:
$ g++ showdate.cc helper.cc –o showdate
如果你想先編譯這些源文件,最后再把它們鏈接成一個(gè)2進(jìn)制文件,可以用-c選項(xiàng),那么g++就只產(chǎn)生object文件,如下所示:
$ g++ -c showdate.cc
$ g++ -c helper.cc
$ g++ showdate.o helper.o –o showdate
$ ls -l
total 124
-rw-r--r-- 1 wvh users 210 Oct 5 12:42 helper.cc
-rw-r--r-- 1 wvh users 45 Oct 5 12:29 helper.h
-rw-r--r-- 1 wvh users 1104 Oct 5 13:50 helper.o
-rwxr-xr-x 1 wvh users 13891 Oct 5 13:51 showdate
-rw-r--r-- 1 wvh users 208 Oct 5 12:44 showdate.cc
-rw-r--r-- 1 wvh users 1008 Oct 5 13:50 showdate.o 注意
所有的GCC編譯器都是通過(guò)文件的后綴名來(lái)判斷文件類型的,然后選擇應(yīng)該進(jìn)行的操作(比如,后綴名為.o的文件只需要進(jìn)行鏈接),文件類型到操作的映射記錄在GCC的specs文件里。在GCC版本4以前,specs文件是標(biāo)準(zhǔn)文本文件,可以用任何文本編輯器修改;但是GCC版本4以后specs文件是內(nèi)建文件,必須要進(jìn)行解壓才能修改。
很顯然,當(dāng)你的項(xiàng)目文件稍微多點(diǎn),使用命令行來(lái)編譯就不可接受了,特別是還要加上搜索目錄、優(yōu)化選項(xiàng)和其他g++選項(xiàng)。解決的方案就是make,不過(guò)本文并不討論它。
C++源文件擴(kuò)展名
前面說(shuō)過(guò)所有GCC編譯器都通過(guò)文件后綴名來(lái)決定采用的操作。下表列出了g++認(rèn)識(shí)的文件類型和相應(yīng)的操作。
Suffix Operation .C C++ source code to preprocess.
.cc C++ source code to preprocess. This is the standard extension for C++ source files.
.cpp C++ source code to preprocess.
.cxx C++ source code to preprocess
.ii C++ source code not to preprocess. 如果一個(gè)文件的后綴名未知,那么就當(dāng)成object文件進(jìn)行鏈接。這并不是說(shuō)你只能使用上表列出的文件名后綴來(lái)區(qū)分源代碼文件和其他文件,你可以用-x lang選項(xiàng)指定一個(gè)或多個(gè)輸入文件的代碼類型,不使用標(biāo)準(zhǔn)的文件名后綴規(guī)則。lang參數(shù)指定代碼的類型;對(duì)于C++,輸入文件可以是c++(標(biāo)準(zhǔn)的C++源文件)或c++-cpp-output(已經(jīng)被預(yù)處理過(guò)的C++源文件,不需再進(jìn)行預(yù)處理)。
注意
當(dāng)GCC編譯器遇到上表列出的文件后綴,它會(huì)當(dāng)成C++文件。但是,有些GCC編譯器(比如gcc)不能處理C++程序里很復(fù)雜的依賴關(guān)系,比如復(fù)雜的類庫(kù),于是編譯失敗。所以你應(yīng)該用g++(或c++)來(lái)編譯C++程序。
GCC的C++編譯器的命令行選項(xiàng)
許多命令行選項(xiàng)對(duì)于GCC編譯器家族都是通用的,下表只列出g++專有的命令行參數(shù)。
g++編譯器的其他一些C++選項(xiàng)處理優(yōu)化、警告和代碼生成的任務(wù),我們?cè)谄渌鹿?jié)里討論。下表總結(jié)了專對(duì)C++的警告選項(xiàng)。
ABI Differences in g++ Versions
C++ ABI是一套API標(biāo)準(zhǔn),定義了C++庫(kù)提供的數(shù)據(jù)類型、類、方法、頭文件等的接口和規(guī)范。對(duì)庫(kù)和目標(biāo)文件來(lái)說(shuō),物理組織、參數(shù)傳遞方式和命名方式是很重要的,所以需要一個(gè)統(tǒng)一的接口,使編譯出來(lái)的C++程序與提供的庫(kù)的接口一致。這種一致性對(duì)語(yǔ)言特有的一些屬性更加重要,比如拋出異常和捕捉異常的時(shí)候。
從版本3開(kāi)始的GNU C++編譯器,都遵循一個(gè)工業(yè)標(biāo)準(zhǔn)的C++ ABI規(guī)范,定義在http://www./cxx-abi/abi.html。雖然這個(gè)規(guī)范是為64位Itanium定制的,但是它適用于任何平臺(tái),并且已經(jīng)作為GNU/Linux和BSD系統(tǒng)的C++ ABI的實(shí)現(xiàn)。
版本3.4以前的g++使用ABI版本1,之后使用ABI版本2。不同ABI版本之間的程序和庫(kù)不能混用。如果你不確定自己g++的ABI版本,可以用g++ --version命令檢查g++的版本,或用一個(gè)偽編譯命令顯示ABI標(biāo)識(shí)符,命令行如下:
g++ -E -dM - < /dev/null | awk '/GXX_ABI/ {print $3}'
如果顯示102,那么就是版本1;如果顯示1002,就是版本2。如果你必須用到以前版本ABI的庫(kù),那么給g++加上選項(xiàng)-fabi-version=n,其中n就是你要兼容的ABI版本。這樣做只能算作權(quán)宜之計(jì),把所有舊的代碼和庫(kù)更新到當(dāng)前版本才是最佳解決方案。
GNU C++ Implementation Details and Extensions
本文雖然不討論怎樣寫(xiě)好C++程序,但是當(dāng)你用GCC的C++編譯器編譯你的C++程序的時(shí)候,你可以從GCC的擴(kuò)展中得到許多好處,包括編譯器自身的優(yōu)勢(shì)和g++使用的標(biāo)準(zhǔn)C++庫(kù)libstdc++的優(yōu)勢(shì)。本節(jié)提煉出最為重要的一些擴(kuò)展特性,并討論它們?cè)贑++規(guī)范和編譯器行為方面的一些差異。
Attribute Definitions Specific to g++
作為對(duì)visibility屬性(詳見(jiàn)于“Visibility Attributes and Pragmas for GCC C++ Libraries”)的補(bǔ)充,g++提供了2個(gè)額外的屬性,即init_priority(priority)和java_interface屬性。
The init_priority Attribute
該屬性允許用戶控制某個(gè)名字空間里的對(duì)象的初始化順序。通常,對(duì)象的初始化順序是它們?cè)谀硞€(gè)代碼單元里的定義順序。init_priority只有一個(gè)整型參數(shù),值為101到65535,越小表示優(yōu)先級(jí)越大。比如,在下面的偽碼里,類MyClass將比類YourClass先初始化:
class MyClass
{
…
};
class YourClass
{
__attribute__ ((visibility("default"))) void MyMethod();
…
};
要改變它們的初始化順序,你可以把代碼改成下面這樣:
class MyClass
{
__attribute__ ((init_priority(65535)));
…
};
class YourClass
{
__attribute__ ((init_priority(101)));
…
};
你只需要注意所使用的優(yōu)先級(jí)數(shù)值的順序,具體使用了哪個(gè)數(shù)值則無(wú)所謂(即只要MyClass的優(yōu)先級(jí)數(shù)值比YourClass大就行了,是不是65535和101則無(wú)所謂)。
The java_interface Attribute
該屬性通知g++某個(gè)類是一個(gè)Java接口類,并只能在標(biāo)識(shí)了extern “Java”的模塊內(nèi)使用。調(diào)用這個(gè)類的函數(shù)使用的是GCC Java編譯器的接口表機(jī)制(interface table mechanism),而不是通常的C++虛函數(shù)表機(jī)制(virtual function table mechanism)。
提示
記住,Java的運(yùn)行時(shí)環(huán)境需要更多的初始化工作。當(dāng)你混合使用C++和Java代碼時(shí),最好用Java寫(xiě)主程序,這樣能保證調(diào)用Java函數(shù)前初始化工作已經(jīng)做足了。
C++ Template Instantiation in g++
模板是C++最有用和最有趣的特性之一,能減少重復(fù)代碼,提高復(fù)用率,簡(jiǎn)化調(diào)試和代碼維護(hù)工作。模板也有利于編譯時(shí)的類型檢查,比如,使用了模板就不用再傳遞void指針,因?yàn)槟憧梢园涯0鍏?shù)實(shí)例化成任何需要的類型。
g++通過(guò)增加3個(gè)功能擴(kuò)展了標(biāo)準(zhǔn)的ISO模板定義:
l 支持使用extern關(guān)鍵詞對(duì)實(shí)例化類型進(jìn)行前置申明;
l The ability to instantiate the support data required by the compiler for a named template class without actually instantiating it by using the inline keyword
l The ability to only instantiate the static data members of a class without instantiating support data or member functions by using the static keyword
基本上,GCC的g++編譯器支持Borland和Cfront(AT&T)兩種模板特性。要支持Borland的模板實(shí)例化和使用特性,g++使用-frepo選項(xiàng)允許預(yù)處理器在處理每個(gè)翻譯單元(源代碼文件)時(shí)進(jìn)行模板實(shí)例化,并把信息存在.rpo文件里。這些文件被后面的編譯過(guò)程使用,并由鏈接器最后合并成單個(gè)編譯單元。要支持Cfront特性,g++內(nèi)置了一個(gè)模板實(shí)例化庫(kù)并在鏈接的時(shí)候合并到代碼里。Cfront要求使用模板的代碼要么進(jìn)行顯式實(shí)例化,要么包含定義模板的申明文件。你可以把顯式實(shí)例化放在代碼的任何地方,或一個(gè)包含的頭文件里。對(duì)于后者,你可能要去掉-fno-implicit-templates選項(xiàng),這樣你只得到了顯式實(shí)例化的那些實(shí)例。
Function Name Identifiers in C++ and C
GCC編譯器預(yù)定義了2個(gè)標(biāo)識(shí)符存儲(chǔ)當(dāng)前函數(shù)的標(biāo)識(shí)。__FUNCTION__標(biāo)識(shí)符只存儲(chǔ)函數(shù)名字,__PRETTY_FUNCTION__則存儲(chǔ)函數(shù)的全稱。在C程序里,這2種名字是一樣的,但是在C++程序里它們有區(qū)別。下面的程序展示了這種區(qū)別:
#include <iostream>
using namespace std;
class c {
public:
void method_a(void)
{
cout<<"Function "<<__FUNCTION__<<" in "<<__FILE__<< endl;
cout<<"Pretty Function "<<__PRETTY_FUNCTION__<<" in "
<< __FILE__ << endl;
}
};
int main(void)
{
c C;
C.method_a();
return 0;
}
運(yùn)行的輸出是:
$ ./a.out
Function method_a in FUNCTION_example.cc
Pretty Function void c::method_a() in FUNCTION_example.cc 在C++里,__FUNCTION__和__PRETTY_FUNCTION__是變量,而不是宏定義,所以#ifdef __FUNCTION__是沒(méi)有意義的。
注意
如果你的GCC是3.2版本或更高,那么__FUNCTION__和__PRETTY_FUNCTION__的行為就和C99定義的__func__變量是一樣的。早于3.2版本的GCC編譯器把__FUNCTION__和__PRETTY_FUNCTION__定義成字符串,所以它們可以和其他字符串進(jìn)行串接操作。
Minimum and Maximum Value Operators
g++編譯器加入了<?和>?操作符,分別表示2個(gè)數(shù)值中較小的和較大的那個(gè)。比如,下面的代碼把10賦給min變量:
min = 10 <? 15;
而下面的代碼把15賦給max:
max = 10 >? 15;
提示
既然這些操作符是語(yǔ)言提供的,那么它們也能對(duì)任何類或enum類型進(jìn)行重載。
Using Java Exception Handling in C++ Applications
Java和C++的異常處理模型是不同的,雖然g++能猜測(cè)C++代碼何時(shí)使用了Java異常,你最好還是明確標(biāo)識(shí)出這種情況,避免鏈接錯(cuò)誤。要告訴g++一塊代碼可能使用Java異常,把下面的代碼放在該翻譯單元中任何catch和throw代碼之前:
#pragma GCC java_exceptions
你不能在一個(gè)翻譯單元里同時(shí)使用Java和C++異常。
Visibility Attributes and Pragmas for GCC C++ Libraries
寫(xiě)C++庫(kù)的時(shí)候一個(gè)普遍的問(wèn)題就是可見(jiàn)的ELF符號(hào)太多了,其實(shí)許多符號(hào)都不能被外部使用,也不用對(duì)外公開(kāi)。GCC版本4.02及更高提供了-fvisibility=value選項(xiàng)和相關(guān)的內(nèi)置屬性,使你可以控制這種行為,使用的方式和微軟C++編譯器提供的__declspec(dllexport)方式相似。新的-fhidden選項(xiàng)有2個(gè)可選值:default,導(dǎo)出目標(biāo)文件的所有符號(hào)(這也是默認(rèn)的行為);hidden,不導(dǎo)出當(dāng)前目標(biāo)模塊的符號(hào)。還可以在函數(shù)或類前加如下代碼來(lái)進(jìn)行設(shè)置:__attribute__ ((visibility("default")))和__attribute__ ((visibility("hidden")))。
默認(rèn)情況下,ELF導(dǎo)出全部符號(hào)。要隱藏特定目標(biāo)文件的符號(hào),需要在編譯該文件的時(shí)候加上-fvisibility=hidden選項(xiàng)。這將導(dǎo)致makefile的復(fù)雜性大大增加,因?yàn)槟阋葱枰謩?dòng)設(shè)置每個(gè)文件的編譯選項(xiàng),要么改變?nèi)志幾g選項(xiàng)導(dǎo)致任何符號(hào)都不能導(dǎo)出。這在類庫(kù)正常拋出異?;蛘哒{(diào)試某些變量的時(shí)候?qū)嵲谑莻€(gè)災(zāi)難。
讓指定的符號(hào)可見(jiàn)的好方式是聯(lián)合使用代碼屬性設(shè)置和編譯選項(xiàng)-fvisibility=hidden。如果要導(dǎo)出某個(gè)符號(hào),先在它們的定義前加上__attribute__((visibility("default"))),比如下面這樣:
class MyClass
{
int i;
__attribute__ ((visibility("default"))) void MyMethod();
…
};
然后給makefile增加-fvisibility=hidden的編譯選項(xiàng),這樣所有其他的符號(hào)就被隱藏了。另一個(gè)稍微好點(diǎn)的方法是定義一個(gè)宏,并放到所有你不想導(dǎo)出的符號(hào)定義前面,然后使用默認(rèn)的導(dǎo)出所有符號(hào),如下所示:
#define LOCAL __attribute__ ((visibility("hidden")))
class MyClass
{
int i;
LOCAL void MyMethod();
…
};
編譯時(shí)不使用-fvisibility=value選項(xiàng),這樣除了MyMethod被隱藏,其他符號(hào)都被導(dǎo)出。
還有一種控制可見(jiàn)屬性的pragma語(yǔ)法現(xiàn)在還能使用,不過(guò)將來(lái)可能要去掉,如下面這樣:
extern void foo(int);
#pragma GCC visibility push(hidden)
extern void bar(int);
#pragma GCC visibility pop 符號(hào)foo會(huì)被導(dǎo)出,但是bar則不會(huì)。這種方式雖然很簡(jiǎn)單方便,但是建議你還是使用visibility和__attribute__。
|
|
來(lái)自: astrotycoon > 《gcc》