c 包含其他文件_C/C++编程笔记:C/C++的编译和链接,计算机专业大学生必备知识...
C/C++文件
C/C++程序文件包括 .h .c .hpp .cpp,其中源文件(.c .cpp)是基本的編譯單元,頭文件(.h .hpp)不會被編譯器編譯。
C/C++項目構(gòu)建(build)過程,分為以下幾個步驟 預處理 → 編譯 → 鏈接。
預編譯
預編譯的過程可以理解為編譯器(實際上是預處理器,這里統(tǒng)稱為編譯器就可以了)在正式編譯之前處理C/C++文件中的預處理命令,即#開頭的代碼。
常用的幾個預處理命令如下:
#include ......
#ifdef ...... #else......#endif
#define ......
#pragma ......
舉個例子,下面是個很簡單的類定義:
MyClass.h
MyClass.cpp
預編譯完成后的樣子:
可以看到編譯器把.h文件替換到了.cpp文件中的#include 位置上,把DEFAULT_VALUE定義的值也替換到了相應的位置。
編譯
預編譯之后,編譯器會編譯每個源文件(.c .cpp),如果編譯成功,會生成對應的目標文件,Linux為.o文件,Windows平臺下為.obj文件。
以Linux平臺為例,上面的MyClass.cpp編譯完成后會生成MyClass.o文件
使用objdump可以看到目標文件MyClass.o的內(nèi)容
編譯器會把MyClass::Fun()的名字改成_ZN7MyClass3FunEv,這個過程叫Mangle,由于C++支持重載,覆蓋等特性,所以編譯器必須把函數(shù)用一個唯一的標識表示。這個字符串就是編譯器生成的唯一標識。
這里還要單獨說一下頭文件,頭文件的既然不是編譯單元,那么它的作用是什么?
頭文件就是負責”聲明“,編譯器在編譯MyClass.cpp的時候,對于MyClass這個類以及Fun()這個成員函數(shù),編譯器必須找到它的聲明,這個函數(shù)才能被正確編譯。
如果有其他cpp需要使用MyClass這個類的時候,也需要它的的聲明。例如
main.cpp
加上#include "MyClass.h" 編譯器在編譯main.cpp的時候才知道怎么編譯MyClass這個類。MyClass.h里聲明是不會真正被編譯到main.o中,.h文件中的內(nèi)容在目標文件中只是以列表的形式存在,這個表在后面鏈接時會用到。
當然,頭文件不僅可以用來聲明,還可以定義(定義全局變量,全局函數(shù)等),在頭文件中的定義要小心,可能會引起鏈接錯誤。
鏈接
鏈接就是將一堆目標文件加靜態(tài)庫文件裝配成可執(zhí)行文件的過程。(或者是裝配成靜態(tài)/動態(tài)庫的過程)
上面兩個cpp分別被編譯成了MyClass.o, 和main.o,我們要生成可執(zhí)行程序的話,就必須經(jīng)過鏈接的過程,把兩個目標文件合成一個可執(zhí)行文件。
main.o中,main函數(shù)會構(gòu)造MyClass, 并且調(diào)用Fun()函數(shù),那么main就根據(jù)MyClass.h生成的表,找到MyClass.o中的函數(shù),這個就是鏈接器要做的工作。
常見錯誤
構(gòu)建c/c++工程的時候,最常見的就是兩種錯誤:
-- 編譯錯誤,在編譯過程中產(chǎn)生的錯誤,通常是語法錯誤,沒有聲明,重復聲明導致編譯目標文件錯誤
其中沒有聲明通常是由于沒有#include相應的頭文件,或者頭文件缺少相應的聲明。
而重復聲明通常是#include了相同的頭文件,比如 B.h 和 C.h 都包含 A.h,然后 main.h 包含了 B.h 和 C.h,這就導致A.h 在main中被包含了兩次。
解決這個問題的方法是可以在所有.h文件的第一行加上
#pragmaonce
或者,使用#ifndef ... #define ... #endif 語句塊
-- 鏈接錯誤,常見的錯誤也是兩種,沒有定義和重復定義,和上面的沒有聲明,重復聲明類似。(這里定義指的就是函數(shù)實現(xiàn))
先討論沒有定義(undefined reference to xxx)
通常是因為函數(shù)有聲明,而且被使用了,但是沒有被定義。比如上面MyClass.cpp中,如果Fun()沒有被實現(xiàn)的話,MyClass.cpp和main.cpp編譯時都不會報錯,但是鏈接時會報告找不到Fun()。
當然,如果Fun()沒被main.cpp調(diào)用的話,即使不實現(xiàn)它,整個構(gòu)建過程也不會出錯,因為鏈接器根本不會去找這個函數(shù)的定義。
然后是重復定義(multiple definition)
指的一份相同的定義在兩個目標文件中都存在,鏈接的時候鏈接器不知道時用哪個了。這種問題通常由于全局函數(shù),和全局變量定義在了頭文件中。導致多個目標文件包含相同的全局函數(shù)和全局變量的定義。
解決方法就是在頭文件中聲明,定義放到cpp文件中,或者為定義加上const 或 static這樣的修飾符,鏈接時會對這些帶有const和修飾符的變量特殊處理的。
const只適用于定義常量變量,static定義的是靜態(tài)全局變量,只在當前cpp有效,所以鏈接它也不會被別的目標文件鏈接,就不會有重復定義的問題了。
總之在頭文件中定義變量和函數(shù)要特別主意,可能會導致鏈接錯誤。
當然也不是所有定義都不能放到頭文件中,比如剛才說的const常量,static全局變量就是例外,還有內(nèi)聯(lián)函數(shù),可以定義在.h文件中,因為內(nèi)聯(lián)函數(shù)會被拷貝到每個目標文件中,也不會參與鏈接的過程。
還有模板類必須放在頭文件中定義,這個下面會討論這個。
關(guān)于模板,靜態(tài)成員變量
模板類模板函數(shù)必須聲明和定義在頭文件中,原因是什么,舉個例子,假設(shè)MyClass是模板類
MyClass.h
MyClass.cpp
main.cpp
編譯的時候沒有問題,但是鏈接時會報錯,main.cpp找不到MyClass::Fun(),如下圖
MyClass雖然定義了Fun函數(shù),但是MyClass.o中存在MyClass::Fun(),而根據(jù)MyClass.h文件,main.o中需要找到MyClass::Fun()的定義
結(jié)果鏈接器哪都找不到,只好報錯了。(實際上通過objdump查看MyClass.o,編譯器都沒有生成MyClass::Fun(),因為編譯器認為這個函數(shù)沒人使用,就直接優(yōu)化掉了)
如果非得在cpp中定義模板類的成員函數(shù)呢,有一種方法就是要顯示的在cpp文件中聲明,比如
MyClass.cpp
加上下面這行就不會有問題了,但是缺點就是開發(fā)MyClass的程序員無從知道其他類是怎么使用這個模板的,不可能把所有可能的模板參數(shù)全都一一的列在這里。
所以模板類的定義還是要寫在.h文件中,
那么如果main.cpp使用到了MyClass, 另外一個cpp也使用到了MyClass,會不會產(chǎn)生重復代碼導致重復定義呢,不會,編譯器會處理好模板類的。
下面是靜態(tài)成員變量,為什么靜態(tài)成員變量的定義要放在cpp里,(模板類的靜態(tài)成員變量除外)
靜態(tài)成員變量和靜態(tài)全局成員變量不同。
靜態(tài)成員變量的作用域可以是整個工程,而靜態(tài)全局變量的作用域只是當前的cpp。所以靜態(tài)成員變量定義在.h中就會發(fā)生重定義錯誤。
想要在程序員生涯內(nèi)有更高的成就的話,C/C++就是一個既可以強化思維能力,又可以打好編程基礎(chǔ)的編程語言,你想要做軟件開發(fā),成為核心程序員的話,學習C/C++的話筆者有一個C/C++的編程倆千人羣(Q艘索:C/C++編程學習13群)你如果感覺自學C/C++語言有困難的話,有興趣學習或者了解一下C/C++編程的小伙伴就可以進來交流。
總結(jié)
以上是生活随笔為你收集整理的c 包含其他文件_C/C++编程笔记:C/C++的编译和链接,计算机专业大学生必备知识...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: linux跨版本升级,深度Deepin系
 - 下一篇: python hook_python_理