天天看點

gcc g++ 支援c++11編譯的标準和差別

g++ -g -Wall -std=c++11 main.cpp

gcc -g -Wall -std=c11 main.cpp

如果不想每次寫這個-std=C++11這個選項該怎麼辦呢?

  方法出處:http://stackoverflow.com/questions/16886591/how-do-i-enable-c11-in-gcc

  方法1:寫Makefile

  方法2:取别名 :alias g++11="g++ -std=c++11"

--------------------------------------------------------------------------------------------------------------------------------

一般而言,在Linux下編譯程式分為以下4個階段:

  1. 預處理:編譯處理宏定義等宏指令(eg:#define)——生成字尾為“.i”的檔案    
  2. 編譯:将預處理後的檔案轉換成彙編語言——生成字尾為“.s”的檔案    
  3. 彙編:由彙編生成的檔案翻譯為二進制目标檔案——生成字尾為“.o”的檔案    
  4. 連接配接:多個目标檔案(二進制)結合庫函數等綜合成的能直接獨立執行的執行檔案——生成字尾為“.out”的檔案

在Linux下執行gcc與g++編譯C++檔案的差别:

  1. 字尾為.c的,gcc把它當作是C程式(cc/cpp才判定為C++源程式),而g++當作是c++程式
  2. gcc無法進行庫檔案的連接配接,即無法編譯完成步驟4;而g++則能完整編譯出可執行檔案。(實質上,g++從步驟1-步驟3均是調用gcc完成,步驟4連接配接則由自己完成)

  gcc -E 執行到步驟1,隻處理宏指令,需要用重定向生成檔案

  gcc -S 執行到步驟2,生成檔案.s

  gcc -c 執行到步驟3,生成檔案.o

  g++ 分别編譯于連接配接 .cc檔案與.o檔案

誤區一:gcc隻能編譯c代碼,g++隻能編譯c++代碼

兩者都可以,但是請注意:

1.字尾為.c的,gcc把它當作是C程式,而g++當作是c++程式;字尾為.cpp的,兩者都會認為是c++程式,注意,雖然c++是c的超集,但是兩者對文法的要求是有差別的,例如:

#include <stdio.h>

int main(int argc, char* argv[]) {

   if(argv == 0) return;

   printString(argv);

   return;

}

int printString(char* string) {

  sprintf(string, "This is a test.\n");

}

如果按照C的文法規則,OK,沒問題,但是,一旦把字尾改為cpp,立刻報三個錯:“printString未定義”;

“cannot convert `char**' to `char*”;

”return-statement with no value“;

分别對應前面紅色标注的部分。可見C++的文法規則更加嚴謹一些。

2.編譯階段,g++會調用gcc,對于c++代碼,兩者是等價的,但是因為gcc指令不能自動和C++程式使用的庫聯接,是以通常用g++來完成連結,為了統一起見,幹脆編譯/連結統統用g++了,這就給人一種錯覺,好像cpp程式隻能用g++似的。

誤區二:gcc不會定義__cplusplus宏,而g++會

實際上,這個宏隻是标志着編譯器将會把代碼按C還是C++文法來解釋,如上所述,如果字尾為.c,并且采用gcc編譯器,則該宏就是未定義的,否則,就是已定義。

誤區三:編譯隻能用gcc,連結隻能用g++

嚴格來說,這句話不算錯誤,但是它混淆了概念,應該這樣說:編譯可以用gcc/g++,而連結可以用g++或者gcc -lstdc++。因為gcc指令不能自動和C++程式使用的庫聯接,是以通常使用g++來完成聯接。但在編譯階段,g++會自動調用gcc,二者等價。

誤區四:extern "C"與gcc/g++有關系

實際上并無關系,無論是gcc還是g++,用extern "c"時,都是以C的命名方式來為symbol命名,否則,都以c++方式命名。試驗如下:

me.h:

extern "C" void CppPrintf(void);

me.cpp:

#include <iostream>

#include "me.h"

using namespace std;

void CppPrintf(void)

{

     cout << "Hello\n";

}

test.cpp:

#include <stdlib.h>

#include <stdio.h>

#include "me.h"        

int main(void)

{

    CppPrintf();

    return 0;

}

1. 先給me.h加上extern "C",看用gcc和g++命名有什麼不同

[[email protected] G++]# g++ -S me.cpp

[[email protected] G++]# less me.s

.globl _Z9CppPrintfv        //注意此函數的命名

        .type   CppPrintf, @function

[[email protected] GCC]# gcc -S me.cpp

[[email protected] GCC]# less me.s

.globl _Z9CppPrintfv        //注意此函數的命名

        .type   CppPrintf, @function

完全相同!

2. 去掉me.h中extern "C",看用gcc和g++命名有什麼不同

[[email protected] GCC]# gcc -S me.cpp

[[email protected] GCC]# less me.s

.globl _Z9CppPrintfv        //注意此函數的命名

        .type   _Z9CppPrintfv, @function

[[email protected] G++]# g++ -S me.cpp

[[email protected] G++]# less me.s

.globl _Z9CppPrintfv        //注意此函數的命名

        .type   _Z9CppPrintfv, @function

完全相同!

【結論】完全相同,可見extern "C"與采用gcc/g++并無關系,以上的試驗還間接的印證了前面的說法:在編譯階段,g++是調用gcc的。

二:gcc和g++的包含頭檔案庫檔案方法

-l參數就是用來指定程式要連結的庫,-l參數緊接着就是庫名,那麼庫名跟真正的庫檔案名有什麼關系呢?就拿數學庫來說,他的庫名是m,他的庫檔案名是libm.so,很容易看出,把庫檔案名的頭lib和尾.so去掉就是庫名了。

好了現在我們知道怎麼得到庫名,當我們自已要用到一個第三方提供的庫名字libtest.so,那麼我們隻要把 libtest.so拷貝到/usr/lib裡,編譯時加上-ltest參數,我們就能用上libtest.so庫了(當然要用libtest.so庫裡 的函數,我們還需要與libtest.so配套的頭檔案)

放在/lib和/usr/lib和/usr/local/lib裡的庫直接用-l參數就能連結了,但如果庫檔案沒放 在這三個目錄裡,而是放在其他目錄裡,這時我們隻用-l參數的話,連結還是會出錯,出錯資訊大概是:“/usr/bin/ld: cannot find -lxxx”,也就是連結程式ld在那3個目錄裡找不到libxxx.so,這時另外一個參數-L就派上用場了,比如常用的X11的庫,它在/usr /X11R6/lib目錄下,我們編譯時就要用-L/usr/X11R6/lib -lX11參數,-L參數跟着的是庫檔案所在的目錄名。再比如我們把libtest.so放在/aaa/bbb/ccc目錄下,那連結參數就是-L /aaa/bbb/ccc -ltest

另外,大部分libxxxx.so隻是一個連結,以RH9為例,比如libm.so它連結到/lib/libm.so.x,/lib/libm.so.6又連結到/lib/libm-2.3.2.so,

如果沒有這樣的連結,還是會出錯,因為ld隻會找libxxxx.so,是以如果你要用到xxxx庫,而隻有libxxxx.so.x或者libxxxx-x.x.x.so,做一個連結就可以了ln -s libxxxx-x.x.x.so libxxxx.so

手工來寫連結參數總是很麻煩的,還好很多庫開發包提供了生成連結參數的程式,名字一般叫xxxx-config,一般放在/usr/bin目錄下,比如

gtk1.2的連結參數生成程式是gtk-config,執行gtk-config --libs就能得到以下輸出"-L/usr/lib -L/usr/X11R6/lib -lgtk -lgdk -rdynamic

-lgmodule -lglib -ldl -lXi -lXext -lX11 -lm",這就是編譯一個gtk1.2程式所需的gtk連結參數,xxx-config除了--libs參數外還有一個參數是--cflags用來生成頭 檔案包含目錄的,也就是-I參數,在下面我們将會講到。你可以試試執行gtk-config --libs --cflags,看看輸出結果

現在的問題就是怎樣用這些輸出結果了,最笨的方法就是複制粘貼或者照抄,聰明的辦法是在編譯指令行裡加入這個 `xxxx-config --libs --cflags`,比如編譯一個gtk程式:gcc gtktest.c `gtk-config --libs --cflags`這樣就差不多了。注意`不是單引号,而是1鍵左邊那個鍵。

5、-include和-I參數

-include用來包含頭檔案,但一般情況下包含頭檔案都在源碼裡用#include xxxxxx實作,-include參數很少用。-I參數是用來指定頭檔案目錄,/usr/include目錄一般是不用指定的,gcc知道去那裡找,但 是如果頭檔案不在/usr/include裡我們就要用-I參數指定了,比如頭檔案放在/myinclude目錄裡,那編譯指令行就要加上-I /myinclude參數了,如果不加你會得到一個"xxxx.h: No such file or directory"的錯誤。-I參數可以用相對路徑,比如頭檔案在目前目錄,可以用-I.來指定。

結論例子:

g++ curltest.cpp -o curltest -L/mnt/hgfs/windows/curl-7.19.5/lib/.libs -lcurl -I/mnt/hgfs/windows/curl-7.19.5/include

繼續閱讀