不同的IDE在編譯代碼時是否存在區別?如果有,那請問區別是什麼?

隱隱約約覺得都是編譯同一種語言,應該沒有區別才對,但真的很想確認不同的IDE之間是否存在區別,以及區別是什麼。

若有,如果可能的話,請以 C++ 語言,IDE:Code::Blocks,Xcode 以及 VC 6.0為例說明。

下面說一下題主個人情況:大一升大二菜鳥,有參加 ACM 競賽的志向,大一隻學了 C,教程為學校自己編製(C 語言程序設計教程,李麗娟主編)教程推薦 VC 6.0,故之前一直用的時 VC 6.0,換了一台 Mac 之後,開始使用 Xcode 與 Code::Blocks 編程,剛入手一本 C++ Primer準備自學。

目前重點是,因為 ACM 競賽所使用的IDE為 Code::Blocks,不知道用 Xcode 與Code::Blocks在編譯 C++ 時有什麼區別。


語言,尤其是像C++這種,確實如你所說,如果都規定死了,那確實所有編譯器都差不多了,

不過萬幸C++標準大部分是語法上的規定,真正實現上,以及優化上並沒有過多規定,就是

為了留給各個編譯器自由發揮.

我工作原因,經常接觸底層的東西,我感覺區別最大的就是優化了.可以說GCC,ICC和msvc

生成的彙編碼簡直天差地別,ICC甚至有時候連邏輯都變了(但結果正確),甚至有一次看了ICC生成的彙編,活活省掉了一條帶lock前綴的彙編指令....驚為天人..最後按照彙編的邏輯把自己的代碼改了.....

舉個簡單的例子:

float* a[I][J];
............
for(int j=0;j&

這是一個矩陣賦值,由於是舉例所以我故意寫了可能會影響優化的代碼,當然編譯器並不知道這是個矩陣賦值操作,那麼最簡單的編譯器,就是按照你寫的循環邏輯來生成彙編就可以了. 不講究優化的編譯器,或者現在的編譯器你開debug模式,這個代碼會生成很糟糕的彙編,雖然結果正確,但運行效率不高.

如何提高效率呢? 首先涉及到變數"k",編譯器可以觀察到在循環內部,雖然名稱一樣的k被聲明了I*J次,但值一直是等於0.01f,因此k變數完全可以在循環外部賦值為0.01,即把代碼邏輯變成:

float* a[I][J];
............
float k=0.01f;
for(int j=0;j&

這樣就能快一些,這個優化其實手動優化就可以了,相信你可能也看過一些書提到這類優化,但是接下來的優化就不是人能參與的了,其中涉及到寄存器的分配,寄存器比內存要快多了. 人一眼就能看出來如果k在循環體內一直存放在寄存器中,那麼循環中就能省去每次從內存中讀取k值的操作,但是編譯器未必能發現,寄存器分配演算法是編譯器中很重要的環節,編譯器很可能發現不了k的潛在邏輯,代碼被變成(偽代碼):

float* a[I][J];
............
float k=0.01f;
for(int j=0;j&

這樣就平白無故多出了I*J次的內存讀取,好的編譯器應該優化成:

float* a[I][J];
............
float k=0.01f; //k一直在寄存器X中,且X在循環中不被其他東西佔用
for(int j=0;j&

我這個例子很簡單啦,但是比這個情況稍微複雜一點,其實有些編譯器已經敗下陣來,

編譯器的寄存器分配演算法差別很大,不好的寄存器分配,生成的彙編碼中充斥著大量毫無意義的寄存器和內存交互的指令,這種彙編一看就不是人寫的.編譯器之間就出現區別了.

但是優化的部分還沒完,我們的a[i][j],你可以看到兩個循環是外層循環j,內層循環i,x86中距離更近的

兩次內存讀取速度比兩次距離超遠的讀取速度快得多.所以理論上應該外層循環i,內層循環j,所以兩個for應該顛倒一下,但是到這一步很多編譯器已經吃不消了! 憑什麼能顛倒? 你怎麼知道顛倒過來沒有破壞代碼本身的邏輯? 這一步,編譯器之間又出現區別了.

再來,我們的數組是float的,照理說會用到FPU,可是實際上這段代碼根本不需要FPU參與,因為本身

沒有數學運算,那到底是選用FPU還是不用? 或者用XMM? SSE? 編譯器又出現區別了.

再喪心病狂一點,可能有的編譯器真的能發現你這個代碼就是矩陣賦值操作,直接整段代碼都替換成

內建的矩陣函數,這就又是區別了.

再補充一下呢,就是上層的一些東西,由於標準並沒有要求如何實現,所以各個編譯器實現的區別也很大,比如std::function,有時候經常寫一些forceinline的函數,一般當標示為forceinline的函數出於某種原因不能內聯時編譯器會有提示.然後,當使用std::function包裹這些函數時,MSVC總是提示我"標示為forceinline的函數未內聯",一看實現能內聯才有鬼呢因為function裡面用到了這個函數的函數指針.

可是ICC從不提示我不能內聯,它遇到forceinline的函數時似乎會生成一個新函數,而且比如你原來的函數有3個參數,綁定了兩個參數,它就會生成一個新的,只有一個參數的函數,其他參數變成了立即數而且按照這個立即數還調整了彙編指令.....這麼一來,都是std::function,這兩個編譯器之間區別就超大了.

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

補充一下關於function的那段,平時不怎麼用function只是試過幾次,關於ICC的實現

描述可能有誤,但意思是那麼個意思,當段子看就行了


有區別的,比如很多年前我搞過一陣子逆向工程,用softice看反彙編,一眼就能看出來這個程序是用 微軟的C++編譯器還是用Borland編譯器生成的。

比如微軟編譯器生產的代碼,每個函數開頭通常是

push ebp

mov ebp,esp

。。。

而borland C++生成的代碼則不是這樣的(具體什麼樣子我忘記了,反正不是微軟那樣的)。

另外,GCC, CL,clang(LLVM) 這種才是編譯器,code:block xcode VisualStudio 是IDE,兩者不是一個東西,通常情況下他們是搭配使用的,但是這個關係不是必然的。

比如 xcode以前用GCC後來換成LLVM。


語義有嚴格定義的語言正確的編譯器編譯出來的代碼只有其運行時間的常數因子有區別。

但是 C++ 語義定義並不嚴格(有大量不確定行為)

而且現在的 C++ 編譯器並不能保證 100% 正確。


//code:

#include&

std::ofstream fout("1.txt");

class hh{

char ID;

public:

hh(char i="z"):ID(i){

fout&<&<"call "&<& }

~hh(){

fout&<&<"call "&<& }

hh(const hh a){

ID=a.ID+1;

fout&<&<"call "&<& }

void operator =(const hh a){

fout&<&<"call "&<& }

};

hh test(hh B){

hh a("a");

return a;

}

int main(){

hh A("A");

A = test(A);

return 0;

}

/*

Result:

Dev-C++ 4.9.9.2:

call A:hh()

call B:hh(const hh)

call a:hh()

call A:operator =(const hh)

call a:~hh()

call B:~hh()

call A:~hh()

Microsoft Visual Studio 2013 Debug:

call A:hh()

call B:hh(const hh)

call a:hh()

call b:hh(const hh)

call a:~hh()

call B:~hh()

call A:operator =(const hh)

call b:~hh()

call A:~hh()

Microsoft Visual Studio 2013 Release:

call A:hh()

call B:hh(const hh)

call a:hh()

call B:~hh()

call A:operator =(const hh)

call a:~hh()

call A:~hh()

*/


當年因為編譯器不同被CE的怒答……

編譯器的任務是將高級語言代碼轉換為機器代碼以供運行,一切編譯器的設計規範都是該語言的語言標準,超出標準限定的部分是由編譯器決定該如何處理的。

C++ Primer 中就舉出了大量編譯器不同帶來編譯結果不同的例子。比如對 char 是否帶符號的處理。


當然有區別。你看對於同一個程序,題主跟我寫出來的代碼,靠譜程度和性能多半是不一樣的對吧。編譯器之間也是同一個道理。人家把你的C++編譯成某個指令集,但是完成一個C++表達式地方法太多了。從這個角度來看,生成的代碼的靠譜程度一般是icc &>&> vc++ &> clang &>&> gcc。


ABI基本上是兩套實現了,也就是名字改編,Microsoft C++(cl)與clang g++ 的不一致,還有C++ Builder的bcc 32位是老式的Broland C++,x64基於clang,ABI的區別就是你函數,變數,類的名字改編了,類型的表示,因為C++有重載。

其他的大夥都說了。


不同編譯器生成最終代碼有各自的風格習慣,破解界常用這個特點人肉或藉助工具(比如PEiD)來分辨目標程序是使用什麼編譯器編譯生成的。


瀉藥。

你說的三個Code::Blocks, Xcode和VC6.0不是編譯器而是IDE,而且第一個的名字都寫錯了。

不同的編譯器生成的可執行文件當然是不同的,甚至同一種編譯器不同版本、不同的優化級別生成的代碼都是不同的。

但是對於「正確沒有歧義」的代碼,執行的結果都是一樣的。不過即使是正確的代碼,遇到編譯器或者標準庫有Bug,結果也可能是錯的。

至於深入點說到造成這種不同的原因,我想邀請@vczh大大回答。

如果樓主僅僅用來刷題,編譯器絕對不是造成你過不了題的原因,所以不用糾結這個問題。不過建議不要用VC6.0,因為在它裡面可以編譯的代碼,在一些OJ上還真編譯不過。


區別大了。

我前幾年專註優化arm代碼。一個程序,用gcc編譯和用realview編譯,realview能做到1/2的大小,速度還快30%

gcc的代碼反彙編尚且能看懂,realview的優化分析起來讓人嘆為觀止。

優秀的編譯器,能做到全局交叉優化,把你的源碼邏輯拆分了重建,按照cpu最優的指令去重寫出來。


不同編譯器編譯同樣的代碼,肯定是有區別的,包括生成的二進位代碼的大小和性能之間的平衡,不同的編譯器都不一樣。這也是不同編譯器之間互相比較的指標。編譯器負責實現輸入程序的語義,至於如何影射到機器指令,沒有固定模式,這就是一門技術了。


擼個cousera 上的那個COOL compiler就都明白了。。。經常是為了讓它能跑起來就什麼都幹得出來了。。。


每種編譯器都會有自己支持的和不支持的語言特性並會對有些代碼進行優化,了解這些特性,能夠讓你不犯錯誤。大部分編譯器的結果是差不多的,主要還是反彙編出來的彙編代碼有所不同,寫了程序之後可以反彙編下看看都生成了什麼代碼


區別在於:

1.對有歧義句子的不同解釋。

2.編譯的效率。

3.目標代碼的效率。


是會不一樣,但是產生的結果是一樣的。就像acm一樣,不關心你每一行代碼寫成什麼樣,關鍵是結果正確,效率還高。畢竟完成一個任務都有不止一種做法。當然最好別去踩坑,有些坑爹的寫法是可能導致有的編譯器出錯的,然後給你一個錯誤的結果。


不同的編譯器生成的代碼絕對不同。

同一個編譯器的不同版本生成的代碼一般也不同。

同一個版本的編譯器不同的編譯選項生成的代碼也不一樣。

區別是什麼? 你讓一萬個人寫同一個命題作文,保證你能得到一萬個不同的結果


推薦閱讀:

C語言為什麼要有 main 函數?具體作用是什麼?
如果scanf的格式指示符是%f,賦給一個double型的變數,在內存層面上會發生什麼?
指針可以修改const修飾的變數么?
int (*pf)(1024)為什麼是函數調用?
為什麼 Windows API 使用 stdcall 調用約定?

TAG:編程 | Xcode | 代碼 | C編程語言 | 編譯器 |