extern陣列與extern指標
陣列名代表了存放該陣列的那塊記憶體,它是這塊記憶體的首位址。這就說明了陣列名是乙個位址,而且,還是乙個不可修改的常量,完整地說,就是乙個位址常量。陣列名跟列舉常量一樣,都屬於符號常量。陣列名這個符號,就代表了那塊記憶體的首位址。注意了!不是陣列名這個符號的值是那塊記憶體的首位址,而是陣列名這個符號本身就代表了首位址這個位址值,它就是這個位址。這就是陣列名屬於符號常量的意義所在。由於陣列名是一種符號常量,它是乙個右值,而指標,作為變數,卻是乙個左值,乙個右值永遠都不是左值,那麼,陣列名永遠都不會是指標!
例如有定義
char a[14];
char * p;
char * q;
void foo(char * pt)
考慮以下幾種賦值:
p=a;//合法,將乙個位址賦給乙個指標變數;
q=p;//合法,將乙個指標變數的值賦給另乙個指標變數;
a=p;//非法,a是陣列名即位址,不是乙個變數,不可被賦值(也就是上文中說的"陣列名是右值")
再看這幾種呼叫:
foo(a);//將乙個位址作為引數傳入函式,函式中用乙個指標變數接收這個位址值
foo(p);//將乙個指標變數的值傳入函式(也是乙個位址),函式中用乙個指標變數接收這個位址
可以看出許多時候陣列名和指標可以等同地看待,而c也把它們看作是相容的型別對待,這就是為什麼我那個錯誤的宣告不被編譯器在語法檢查的時候「喀嚓」的原因。
關於extern的作用,許多地方都有說明,例如可以在c++裡進行c格式函式的宣告,可以宣告乙個變數或函式是外部變數或外部函式;我們這裡要討論的是外部變數的宣告。被extern修飾的全域性變數不被分配空間,而是在連線的時候到別的檔案中通過查詢索引定位該全域性變數的位址。
有了這些基礎後,我們現在正式開始研究extern 陣列和extern 指標的問題:
首先在乙個.c檔案中有如下定義:
char a=;
然後我們在另乙個.c檔案中分別以不同的形式進行宣告:
(1) extern char a;
分析:這是乙個外部變數的宣告,它宣告了乙個名為a的字元陣列,編譯器看到這個宣告就知道不必為這個變數分配空間,這個.c檔案中所有對陣列a的引用都化為乙個不包含型別的標號,具體位址的定位留給聯結器完成。編譯完成之後也得到乙個中間檔案,聯結器遍歷這個檔案,發現有未經定位的標號,於是它搜尋其他中間檔案,試圖尋找到乙個匹配的空間位址,在此例中無疑聯結器將成功地尋找到這個位址並將此中間檔案中所有的這個標號替換為聯結器所尋找到的位址,最終生成的可執行檔案中,所有曾經的標號都應當已經被替換為位址。這是乙個正常工作過程,連線出來的可執行檔案至少在對於該陣列的引用部分將工作得很好。
(2) extern char * a;
分析:這是乙個外部變數的宣告,它宣告了乙個名為a的字元指標,編譯器看到這個宣告就知道不必為這個指標變數分配空間,這個.c檔案中所有對指標a的引用都化為乙個不包含型別的標號,具體位址的定位留給聯結器完成。編譯完成之後仍然得到乙個中間檔案,聯結器遍歷這個檔案,發現有未經定位的標號,於是它搜尋其他中間檔案,試圖尋找到乙個匹配的空間位址,經過一番搜尋,找到了乙個分配過空間的名為a的地方(也就是我們先定義的那個字元陣列),聯結器並不知道它們的型別,僅僅是發現它們的名字一樣,就認為應該把extern宣告的標號連線到陣列a的首位址上,因此聯結器把指標a對應的標號替換為陣列a的首位址。這裡問題就出現了:由於在這個檔案中宣告的a是乙個指標變數而不是陣列,聯結器的行為實際上是把指標a自身的位址定位到了另乙個.c檔案中定義的陣列首位址之上,而不是我們所希望的把陣列的首位址賦予指標a(這很容易理解:指標變數也需要占用空間,如果說把陣列的首位址賦給了指標a,那麼指標a本身在**存放呢?)。這就是癥結所在了。所以此例中指標a的內容實際上變成了陣列a首位址開始的4位元組表示的位址(如果在16位機上,就是2位元組)。本例中指標a的初值將會是0x0a090807(little endian),顯然不是我們的期望值,所以執行會出錯也就理所應當了。
幾點細節:我們發現,使用extern修飾的變數在連線的時候只找尋同名的標號,不檢查型別,例如如果我們定義的甚至不是乙個變數而是乙個全域性的函式,比如去掉定義
char a=;
代之以void a(){};
聯結器居然也會連線通過。
例項如下:
比如在a.c檔案中有這樣一段**
int g_i = ;
extern void testdotp();
void main(void)
int i = 0;
int num = 0;
num = sizeof(g_i) / sizeof(int);
for (i = 0; i < num; i++)
printf("g_i[%d] = %d ", i, g_i[i]);
printf("/n");
testdotp();
而在b.c中的**如下:
extern int *g_i;
void testdotp()
printf("*(&g_i + 2) = %d/n", *(&g_i + 2));
printf("&g_i = %d/n", &g_i);
printf("&g_i + 1= %d/n", &g_i + 1);
printf("g_i = %d/n", g_i);
printf("g_i + 1 = %d/n", g_i + 1);
執行結果為
g_i[0] = 1 g_i[1] = 2 g_i[2] = 3 g_i[3] = 4
*(&g_i + 2) = 3
&g_i = 4344368
&g_i + 1= 4344372
g_i = 1
g_i + 1 = 5
分析如下:
因為b.c檔案中g_i變數的位址是a.c檔案中g_i陣列的首位址,故g_i的值為g_i[0]的值,&g_i的值為g_i位址的首位址。
extern陣列與extern指標
陣列名代表了存放該陣列的那塊記憶體,它是這塊記憶體的首位址。這就說明了陣列名是乙個位址,而且,還是乙個不可修改的常量,完整地說,就是乙個位址常量。陣列名跟列舉常量一樣,都屬於符號常量。陣列名這個符號,就代表了那塊記憶體的首位址。注意了!不是陣列名這個符號的值是那塊記憶體的首位址,而是陣列名這個符號本...
extern陣列與extern指標
陣列名代表了存放該陣列的那塊記憶體,它是這塊記憶體的首位址。這就說明了陣列名是乙個位址,而且,還是乙個不可修改的常量,完整地說,就是乙個位址常量。陣列名跟列舉常量一樣,都屬於符號常量。陣列名這個符號,就代表了那塊記憶體的首位址。注意了!不是陣列名這個符號的值是那塊記憶體的首位址,而是陣列名這個符號本...
extern陣列與extern指標
陣列名代表了存放該陣列的那塊記憶體,它是這塊記憶體的首位址。這就說明了陣列名是乙個位址,而且,還是乙個不可修改的常量,完整地說,就是乙個位址常量。陣列名跟列舉常量一樣,都屬於符號常量。陣列名這個符號,就代表了那塊記憶體的首位址。注意了!不是陣列名這個符號的值是那塊記憶體的首位址,而是陣列名這個符號本...