在android的external/sqlite/android目錄,查看sqlite3_android.h的時候,發現
what that means ?
(首先,涉及到條件編譯,應該是和編譯器有關;
涉及到__cplusplus宏,據我所知,應該和鏈接器ld也關,畢竟c和c++鏈接庫的時候方式不同,鏈接的庫類型不一樣)
把問題搞清楚,先回顧一下extern:(和auto一樣,是一種存儲類型)
在馬偉《改善C建議125》的第8個建議裏面說了extern這個存儲類型:(我簡單概括一下)
函數外面定義的叫外部變量 (也是全局變量) —–引用它的文件是可以修改它的值的(很容易造成混亂)
-----生命週期(時間意義上)是整個程序的生存週期,作用域是文件作用域(聲明的地方到文件尾)
函數內部定義的叫做內部變量(也就是局部變量)—–作用域在函數塊兒內
常見的兩個使用場景是:
使用先於,聲明(在文件的後半部分才定義某個全局變量,卻要在之前就定義的main或者其他函數中使用該變量,這個時候,使用的時候就要加上extern做聲明),舉例?略(又不是入門,所以不給案例)
一個源文件要引用另一個文件定義的全局的變量(自己懶得定義或者想共用一個傳遞&通信信息),這個時候引用該變量的文件就要加上extern關鍵字去聲明引用(不用初始化,因爲是全局變量,程序起來的時候,就已經初始化過了)—案例?略
外部函數:
上面是說的extern的變量,函數也是類似。
默認聲明&定義的時候,只要不加上static,那麼全局的函數或者變量都是extern的,即使你省略關鍵字。
這個需要驗證一下,請看案例如下:(同目錄下有倆文件,tmp.c和main.c)
注意,我並沒有在main.c裏面在預處理的時候include(包含) tmp.c相關聲明&定義。
這裏補充說一下static, 如果你去把全局變量或者全局函數定義成static的那麼其生命週期不變,但是作用域就變成文件作用域了。
可以看下,還是上面的例子,我在tmp.c,也就是定義&聲明的地方,故意加上static,編譯看看
(因爲是鏈接的時候做處理,所以gcc統一處理一下就可以了)
我怎麼去看是預處理還是鏈接的出問題的?
上面就說明是鏈接階段出問題(具體請參看gcc手冊,跟你編譯環境有關)
總結:
被引用的全局變量,在引用的地方加上extern外(僅僅是聲明,並不分配空間!),還要求在定義的地方必須是extern的
(默認就是extern存儲類型,告訴編譯器別人可以引用)
只引用一個函數的話,就沒有去include相應的頭文件,因爲unix的哲學是kiss (keep it simple, and stupid)
extern說完了(告訴編譯器外部模塊可以引用的意思),再說說“C”,這個“C”不能單獨理解,要和extern一起理解
爲什麼?
因爲上面猜想,不是編譯階段,就是鏈接階段的問題,實際上呢?both!
經過在網上查看了一下資料,並親手動手實驗了一下(編譯器gcc 4.9,這裏試驗要寫一個cpp纔可以,用到g++,不貼出來了),
確認了是爲了在鏈接的時候能讓鏈接器ld找到外部函數或者變量的定義,但是鏈接的時候c和c++不同,說明編譯的時候,c和c++的編譯處理規則就不同。
(符號表中找不到,ld肯定報錯啊)
這裏涉及到編譯&鏈接等知識,
我不展開,就說理解(下面有親自試驗):(請注意到一個細節,一般是Cpp程序中才會出現extern “C”)
c++畢竟涉及到的特性多,所以實際編譯出來的代碼的話,會有更多符號(符號表更大),所以在C++文件中
混入C代碼(函數,變量,庫)的時候,要調用原生的C,並且想用C的編譯&鏈接方式處理的那些代碼的話,就要寫上extern “C”,定義和引用的位置都需要。
(注意extern “C”只出現在cpp程序中,c程序,根本不需要extern “C” 只需要extern就可以了,即定義的時候省略extern關鍵字就是extern類型,而引用的時候加上extern即可,而不必extern “C”)
請看頭文件和它的實現文件(注意這是cpp文件)
總結?
關於 extern “C”類型的變量和函數的定義&引用,無非是要在cpp程序中引入c的原生代碼(c的代碼編譯鏈接當然也按c的方式來咯)。
所以你就把extern “C”理解成一種“在C++環境下的定義和引用原生C的全局變量&函數的”類型。
(畢竟像C++這種本來是面向對象特性的語言,偏偏要照顧C這種帶外部變量和函數的語言,一切都是歷史原因,多說無益啊)
歡迎學習c語言c++的小夥伴進c交流學習羣552838805教程和各種資料和工具都放羣裏了歡迎來和小夥伴們一起交流學習進步走上人生巔峯。