此系列是為那些讀過tc++pl或者具有類似水平的同學準備的,作為系列的第一篇以及有趣的熱身,我們來看乙個鏈結問題:
d1.cpp
#include
<
stdio.h
>
struct
x ;x::x(
int=0,
int=0)
class
d: public
x ;
intminus(
inta,
intb)
d2.cpp
#include
<
stdio.h
>
struct
x ;x::x(
int=0)
class
d: public
x ;int
add(
inta,
intb)
用來執行的main.cpp:
#include
<
stdio.h
>
intminus(
inta,
intb);
intadd(
inta,
intb);
intmain()
將以上三個cpp檔案分別編譯並鏈結成乙個應用程式,在執行之前,請先猜個結果。然後執行。
好吧,我想你看到了答案。在你開始思考為什麼會是這樣的之前,請再做一件事,將d1.cpp和d2.cpp兩個檔案的內容完全交換,然後重新編譯執行一次。
請原諒上面寫的比較混亂且沒有給出每一步的結果,因為使用vs的c++編譯器,這個結果是不確定的。然而毫無疑問,兩次執行的結果將是不同的!
在我這裡,第一次顯示了22
第二次顯示了11
omg,檔案居然影響了程式結果!所以請不要總是相信編譯器,這個例子來自c++標準,它用以說明乙個重要的準則:one defination rule,簡寫odr
odr在c++標準中被解釋為:
1.任何編譯單元都不能包含變數、函式、列舉、類或者模板的定義一次以上。
2.所有程式必須且只能包含一次其中用到的所有非內聯函式和物件。
3.在需要類的完整定義的編譯單元中,類的定義必須且只能出現一次。
4.(好bt的一條啊,恰好這一條可以解釋我們的程式)包括類、列舉、類模板......(具體有哪些請自己看spec)在內的一些定義可以在乙個程式中出現多次,但是必須滿足以下條件:
(1)所有定義的token序列必須相同(token你可以認為就是有效的語言要素,出了空白、換行注釋之類的)
(2)所有的命名查詢必須指向同乙個實體,也就是說,你不能搞一些命名空間 typedef之類的,讓這些相同的token表示不同的意義
(3)所有運算子必須表示同乙個過載
(4)對於你要定義的實體中的所有帶預設引數的函式,預設引數必須滿足以上三條
(5)對於類定義,建構函式中呼叫的基類建構函式必須是同乙個
總而言之,這個第四條的意思就是不同的定義之間不能有任何歧義
ps.g++似乎做了跟vc++相同的事情,表現基本一致。
from:
C ,那些可愛的小陷阱(二)
這一次,是關於宣告的乙個小問題 include stdio.h intj 24 int main 這能通過編譯嗎?能 這不會產生未定義行為嗎?不會 這程式甚至不是ill formed ill formed就是c 支援但不推薦的寫法 輸出的結果是 24 42 這個問題並不難猜到答案,但是大概大部分同學...
C ,那些可愛的小陷阱(三)
我們沿襲忠於標準的傳統,還是首先來看乙個標準中的例子 define arraycheck a,b a?b?b?a?這真是一段xe的 你看懂什麼意思了麼?好吧這次厚道點立刻上答案 define arraycheck a,b a b b a 這個 儘管是用來演示三元轉義符的,但是我看到這個巨集定義暗示另...
C ,那些可愛的小陷阱(二)
這一次,是關於宣告的乙個小問題 include stdio.h intj 24 int main 這能通過編譯嗎?能 這不會產生未定義行為嗎?不會 這程式甚至不是ill formed ill formed就是c 支援但不推薦的寫法 輸出的結果是 24 42 這個問題並不難猜到答案,但是大概大部分同學...