gcc g++支援C++11 標準編譯及其區別

weixin_33912246發表於2019-01-08

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++命名有什麼不同
[root@root G++]# g++ -S me.cpp
[root@root G++]# less me.s
.globl _Z9CppPrintfv        //注意此函式的命名
        .type   CppPrintf, @function
[root@root GCC]# gcc -S me.cpp
[root@root GCC]# less me.s
.globl _Z9CppPrintfv        //注意此函式的命名
        .type   CppPrintf, @function
完全相同!
               
2. 去掉me.h中extern "C",看用gcc和g++命名有什麼不同
[root@root GCC]# gcc -S me.cpp
[root@root GCC]# less me.s
.globl _Z9CppPrintfv        //注意此函式的命名
        .type   _Z9CppPrintfv, @function
[root@root G++]# g++ -S me.cpp
[root@root 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

相關文章