const變量能被其他檔案extern引用嗎?為什麼?
先來看一段代碼:
// main.cc
#include<stdio.h>
// 引用外部定義的const_int變量
extern const int const_int;
int main()
{
printf("const_int:%d\n",const_int);
return 0;
}
// const.cc
// 定義const 變量
const int const_int = 10;
編譯連結:
$ g++ -o main main.cc const.cc
/tmp/ccWHAXxB.o: In function `main':
main.cc:(.text+0x6): undefined reference to `const_int'
collect2: error: ld returned 1 exit status
我們發現出現了連結問題,說const_int沒有定義的引用,但我們确實在http://const.cc檔案中定義了。
我們去掉const修飾符再編譯一次,發現是可以的。從上面這個編譯問題,就引出今天要講的内容了。至于為什麼會編譯不過,最後再做解釋。
當然你會發現,按照C代碼去編譯,是可以編譯出來的。後面再解釋。
連結屬性
我們都知道,C/C++代碼的編譯通常經過預編譯,彙編,編譯,連結(參考hello程式是怎麼生成的)通常會有變量會有三種連結屬性:外部連結,内部連結或無連結。
具有函數作用域,塊作用域或者函數原型作用域的變量都是無連結變量;具有檔案作用域的變量可以是内部連結也可以是外部連結。而外部連結變量可以在多個檔案中使用,内部連結變量隻能在一個編譯單元中使用(一個源代碼檔案和它包含的頭檔案)。
關于作用域,也可以參考《全局變量,靜态全局變量,局部變量,靜态局部變量》。
說了這麼多,舉個具體的例子:
#include<stdio.h>
int external_link = 10; // 檔案作用域,外部連結
static internal_link = 20; // 檔案作用域,内部連結
int main()
{
int no_link = 30; // 無連結
printf("%d %d %d \n",external_link,internal_link,no_link);
return 0;
}
這裡無連結變量還是比較好區分的,隻要不是檔案作用域的變量,基本是無連結屬性。而檔案作用域變量是内部連結還是外部連結呢?隻要看前面是否有static修飾即可。當然對于C++,還要看是否有const修飾,後面我們再說。
如何知道某個變量是什麼連結屬性?
舉個例子,在前面的代碼中,我們按照C代碼進行編譯:
$ gcc -c const.c
$ nm const.o |grep const_int
0000000000000000 R const_int
nm指令在《linux常用指令-開發調試篇》中略有介紹,它可以用來檢視ELF檔案的符号資訊。
從這裡的結果可以看到const_int前面是R修飾的,
R:該符号位于隻讀資料區,READONLY的含義
而該字母大寫,其實也是表示它具有外部連結屬性。
再看看按照C++代碼編譯:
$ g++ -c const.c
$ nm const.o |grep const_int
0000000000000000 r _ZL9const_int
可以看到,它的修飾符也是r,但是是小寫的r,小寫字母表示該變量具有内部連結屬性。
nm指令非常實用,但本文不是重點。
const關鍵字
說到const關鍵字,在《const關鍵字到底該怎麼用》和《C++中的const與C中的const有何差别?》中已經分析過了,這裡簡單說一下,被const關鍵字修飾的變量,表明它是隻讀的,不希望被修改。
extern關鍵字
extern關鍵字可以引用外部的定義,想必很多朋友已經很熟悉了,舉個例子,如果把最開始的例子中的const關鍵字去掉,http://main.cc中的extern的意思,就是說有一個const_int變量,但是它在别的地方定義的,是以這裡extern修飾一下,這樣在連結階段,它就會去其他的編譯單元中找到它的定義,并連結。
當然,還有一個不太被關注的作用是,在C++中,它可以改變const變量的連結屬性。
是的,在C++中,它改變了const_int的連結屬性。我們可以修改const.c的内容如下:
extern const int const_int = 10;
然後再檢視一下:
$ nm const.o |grep const_int
0000000000000000 R const_int
發現沒有,它前面的修飾變成大寫的R了,是以這個時候,你再編譯,就能編譯過,而不會報錯了,對于C,它本來就是外部連結屬性,是以根本不會報錯。
extern還有另外一個用法:
《C++是如何調用C接口的》?
解疑
- 未定義
- 在其他地方定義了,但是不具備外部連結屬性
- 定義了,具備外部連結屬性,但是連結順序有問題
總結
- 作用域 --《全局變量,靜态全局變量,局部變量,靜态局部變量》
- const關鍵字--《const關鍵字到底該怎麼用》
- extern關鍵字
- nm檢視符号表