2011-03-17 93 views
4

我最近已經意識到,我不知道,通常來說,如何一個C/C++編譯器的工作原理。我承認這個最初來自試圖理解頭球衛隊,但卻意識到我缺乏編輯工作。瞭解C++編譯

以Visual C++爲例; Theres「頭文件」文件夾,「資源文件」文件夾和「源文件」文件夾。對這些文件夾的分離以及你放入的內容有什麼意義?對我來說,他們都是源文件。就拿代碼片段:

片段1

//a1.h 
int r=4; 

//a1.cpp 
int b //<--semicolon left out on purpose 

//main.cpp 
#include <iostream> 
#include "a1.h" 
void main() 
{ 
    cout << r; 
} 

的編譯器錯誤出來說:「a1.cpp(3):致命錯誤C1004 :發現意外的文件結束「我期望它不會因爲a1.cpp文件不包含主要方法存在其中在下一代碼段

片段2

//a1.h 
int r=4 //<--semicolon left out on purpose 

//a1.cpp 
int b = 4; 

//main.cpp 
#include <iostream> 
void main() 
{ 
    cout << b; 
} 

錯誤了,因爲「main.cpp中(6):錯誤C2065: 'b':未聲明的標識符「。如果你有像這樣

片段的a1.cpp 3

//a1.h 
int r=4 //<--semicolon left out on purpose 

//a1.cpp 
int b = 4; 

//main.cpp 
#include <iostream> 
#include "a1.cpp" 
void main() 
{ 
    cout << b; 
} 

編譯器會抱怨 「a1.obj:錯誤LNK2005:」 int b「(?b @@ 3HA)已在main.obj中定義」。片段2和3都忽略了int r = 4沒有缺少分號的事實,因爲我懷疑它與它的xxxx.h文件有關。如果我從片段1上的項目中刪除a1.cpp文件,那麼它編譯得很好。顯然,我所期望的並不是我所得到的。 Theres提供了大量有關如何在cpp中編寫代碼的書籍和教程,但cpp處理complition過程中的文件和源代碼的方式並不多。這到底是怎麼回事?

+2

您需要對頭文件和源文件之間的區別以及「項目」(或Makefile)的含義有一個非常基本的理解。你可以從閱讀K&R書開始?它不是C++,但你的代碼也不是 - 它是純C(除了「stuff」)。 – Arkadiy 2011-03-17 17:19:34

+2

我總是建議新手到C/C++的人在移入IDE之前使用命令行編譯器並手動鏈接代碼。瞭解編譯/鏈接/執行過程在C/C++中非常重要。坐下來,放鬆一下,GCC一會兒。 – Brandon 2011-03-17 17:26:05

+0

@Brandon你的意思是在linux/unix環境中?或者會有一個Windows端口?另外,我想看什麼樣的文學作品? – 2011-03-17 21:46:07

回答

6

你的問題是不是真正的編譯器,但如何您的IDE正在處理整個構建系統。大多數C/C++項目的編譯系統分別編譯每個.c.cpp文件,然後將生成的目標文件鏈接到一起形成最終的可執行文件。在你的情況下,你的IDE正在編譯文件擴展名爲.cpp的項目中的任何文件,然後鏈接生成的對象。您所看到的行爲可以解釋如下:

  1. a1.cpp缺少;,所以當IDE試圖編譯文件,你得到一個關於「意外的文件結尾」錯誤。

  2. b未在main.cpp編譯單元中的任何位置聲明,因此您會收到有關未定義標識符的錯誤。

  3. b存在於main.cppa1.cpp編譯單元都(明顯a1.cpp,並通過您的#includemain.cpp)。您的IDE編譯這兩個文件 - 現在a1.omain.o每個都包含一個名爲b的對象。鏈接時,會出現重複的符號錯誤。

重要的一點帶走這裏,這也解釋了所有你看到的行爲,那是你的IDE編譯.cpp文件 - 不只是main.cpp,它包括文件 - 然後將得到的對象鏈接。

我建議使用您自己創建的makefile來設置一個命令行測試項目 - 這將向您介紹有關構建系統內部工作的所有信息,然後您可以將這些知識應用於IDE的內部工作。

3
  1. 頭文件不會被編譯
  2. #include指令字面上粘貼可納入文件,而不是#include行
  3. 所有源文件(主redargless)的內容被編譯成的.o或。 obj文件。
  4. 如果有任何obj文件連同外部.lib文件鏈接在一起
  5. 您獲得一個可執行文件。

關於第2點:

example 
//a.h 
int 

//b.h 
x = 

//c.h 
5 

//main.cpp 
#include <iostream> 
int main() 
{ 
#include "a.h" 
#include "b.h" 
#include "c.h" 
; 

std::cout << x << std::endl; //prints 5 :) 
} 

這不是一個完整的答案,但HTH,MY2C等:)

0

編譯器從您告訴的地方拾取源文件。在Visual C++的情況下,有一個IDE告訴編譯器該做什麼,不同的文件夾在那裏,因爲這是IDE如何組織文件。

另外,片段2中的錯誤來自鏈接器,而不是來自編譯器。 編譯器已將main.cpp和a1.cpp編譯爲目標文件main.obj和a1.obj,然後鏈接程序試圖將這些目標文件組合成可執行文件,但變量b在a1.obj(直接)和main.obj(通過包含a1.cpp),所以你得到了「已定義」的錯誤。

1

#include語句將該文件插入到使#include生成的文件中。你的代碼片段3 main.cpp在編譯之前就變成了以下代碼。

// main.cpp  
    // All sorts of stuff from iostream 
    //a1.cpp 
    int b = 4; 
    void main() 
    { 
     cout << b; 
    } 

你得到一個鏈接器錯誤的原因是你定義b兩次。它在a.cpp和main.cpp中定義。

您不妨閱讀關於聲明和定義。

0

您在案例1和案例3中看到的問題與特定VS有關。 VS顯然試圖編譯main.cpp和a1.cpp。情況1:由於VS試圖編譯a1.cpp,它有一個語法錯誤(缺少分號),編譯失敗。

案例2:您尚未在main.cpp或任何包含的文件中聲明變量b。因此編譯失敗。

案例3:這是一個鏈接器錯誤。由於包含,int b已在main.cpp和a1.cpp中聲明。由於它們都不是static或extern,所以具有相同標識符的兩個全局變量已在同一範圍內聲明。這是不允許的。

1

你告訴構建系統要編譯的文件。在Visual C++的情況下,它會自動編譯任何名爲「* .cpp」的文件,並將其添加到項目中。雖然你可以進入項目設置並告訴它不要。

它將編譯名爲* .h文件(儘管它可以的,如果你明確地告訴它。

#include指令,是一些編譯器處理之前它的任何編譯(這就是所謂的它基本上把它指向的文件粘貼到正在編譯的源文件中,#include指令出現在文件中,然後編譯器將整個文件編譯爲一個完整的單元。因此,在您的示例中:

片段1

博特a1.cpp和main.cpp中由編譯系統seperately編譯。所以,當它遇到錯誤om a1.cpp時,它會報告它。

片段2

注意它分開編譯這些文件,沒有彼此的知識,所以當你的參考B在main.cpp中,它不知道B是a1.cpp定義。

片段3

現在你已經包含在main.cpp中a1.cpp,所以它編譯main.cpp中,看到一個定義爲B和說,好,我必須在全球範圍內A B。然後它編譯a1.cpp,並說OK,我在全局範圍內有一個b。

現在鏈接器中的步驟和嘗試將a1和main放在一起,它現在告訴你,嘿,我有2個b的全局範圍。不好。

2

既然看起來有兩種理解你的問題的方法,我會回答理解C++編譯部分。

我建議你首先閱讀Wikipedia中的「編譯器」定義。之後,嘗試谷歌搜索編譯器教程,以建立關於編譯器的複雜性。更具體到C++,你可以閱讀約#include和預處理器指令(嘗試谷歌搜索這些條款。)

如果你仍然想進一步理解編譯器,我建議一本編譯器書。你會在StackOverflow上找到一本好書。

+0

其實訂購了另一篇文章引用的「龍書」,只是等待郵件:) – 2011-03-17 19:00:42

+0

@hydroparadise:不錯的選擇。它很密集,但很好解釋。 – plmaheu 2011-03-17 19:12:11