乙個 inline 函式引發的葫蘆案

2021-04-12 23:47:39 字數 1354 閱讀 5343

乙個同事在檢查另外乙個同事的**時,突然發現乙個 inline 函式在不同的 cpp 檔案裡面都有定義,而且,在另外乙個cpp裡面也引用了這個inline函式。示意**如下:

// a.h

inline extern void foo();

// a.cpp

#include 

#include "a.h"

inline void foo();

// b.h

inline extern void foo();

// b.cpp

#include 

#include "b.h"

inline void foo();

// c.cpp

#include "a.h"

int main();

一開始覺得很奇怪,怎麼定義了2個一摸一樣的inline函式呢 ? 而且在vc下面編譯的時候居然沒有報錯,更加神奇的,是在鏈結的時候也沒有報錯。。。

1、當 inline 函式被宣告為 extern 或者在定義該inline函式的翻譯單元(tu)之外也有對該函式的引用的時候,該函式就會被作為乙個特別的函式來生成**(類似於一般的函式),而不是一段嵌入的**(最簡單的inline函式那種)。這就解釋了為什麼能夠編譯成功。

2、在編譯成功後要進行鏈結啊,比如上面的例子,c.cpp裡面的foo引用要跟a.cpp或者b.cpp裡面的foo定義鏈結啊,按照上面1的情況,會有2個foo的定義,這會導致「定義歧義」的錯誤,但是為什麼這裡沒有報錯呢 ?大家估計可以猜得到,肯定是鏈結器只鏈結了其中乙個版本,比如a.obj或者b.obj裡面得版本。這個是什麼機制呢 ?

3、答案就在前面第1點裡面,當被作為乙個特別的函式生成目標**的時候,這個「特別的函式」的特別地方就是它的目標**被標誌為「延遲鏈結」,同時被被標誌為在重定位的時候「pick any」,這就意味著我們所定義的foo函式是延遲鏈結,最後從2個foo定義裡面取乙個進行重定位,生成可執行檔案。這就解釋了為什麼能夠鏈結成功而不報錯誤。

4、既然是「pick any」,如果沒有明確的鏈結順序指定給鏈結器(linker),則很容易產生預期結果和實際結果不一致的情況(linker充當一下葫蘆官),比如,上面的例子,你用 cl  /out:c.exe a.cpp   c.cpp    b.cpp得到的 c.exe 和 cl  /out:c2.exe   b.cpp   c.cpp  a.cpp 得到的 c2.exe 是不一樣的,可以試著修改一下 b.cpp 裡面的 foo 函式,就知道修改檔案的次序也會導致結果的不同。

5、這個例子是乙個典型的「code of bad **ell 」,這些**導致修改了foo函式的修改不能如實地按照預期在最終的可執行檔案裡面得到體現,導致**和結果的不一致,如果栽在這樣的問題當中,估計無論是誰都會很鬱悶。

乙個printf函式引發的宕機

今天同事解決了乙個問題,頗有啟發,拿出來共賞之。char str printf str 如果這樣寫,會編譯不過 printf str s n 因為引數不夠。可是你有沒有考慮過下面這種情況?char str im a good man s n printf str 這段程式碼是可以編過並執行的,但是。...

乙個memset引發的血案

前幾天做了一道bst題,提交了幾次都是wa,今天抽空拿了出來仔細瞧瞧總算被我發現禍頭根源.總結原因還在於自己對memset不太了解,以前用對估計也是瞎貓撞見死耗子 memset的介紹 void memset void buffer,int ch,size t count buffer 指向某段記憶體...

乙個分號引發的「血案」

再多的表情也無法詮釋我現在的心情!a b for matrices 這是很水的一道題,然而卻整整折騰了我2個多小時。從晚上6點多開始,花了沒幾分鐘就把 敲好了,可是資料一測,竟然不對,然後就開始找問題,找了很久,我竟然都還沒看出問題在哪,越找心裡越不爽,這麼做明明對的呀,一執行怎麼就錯了呢?一直到了...