日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

extern数组与extern指针

發布時間:2023/11/27 生活经验 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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[]={1,2,3,4};

分析:這是一個數組變量的定義,編譯器將為這個數組分配4 字節的空間,并且建立一個索引,把這個數組名 、數組類型和它被分配的空間首地址對應起來。它被編譯之后生成一個中間文件

然后我們在另一個.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 的初值將會是0x0a090807little endian ),顯然不是我們的期望值,所以運行會出錯也就理所應當了。

?

幾點細節:我們發現,使用extern 修飾的變量在連接的時候只找尋同名的標號,不檢查類型,例如如果我們定義的甚至不是一個變量而是一個全局的函數,比如去掉定義

char a[]={....};

代之以

void a(){};

連接器居然也會連接通過。

實例如下:

比如在 a.c 文件中有這樣一段代碼

?

int g_i[] = {1, 2, 3, 4};

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 地址的首地址。

?

*(&g_i + 2) 的值: &g_i 的值為 g_i 數組的首地址, (&g_i + 2) 就為數組 g_i 3 個元素的地址, *(&g_i + 2) 就為第 2 個元素的值,即 3

?

&g_i + 1 :由于 &g_i 的值為 g_i 數組首地址,由于在 32 位機上運行,故 &g_i + 1 &g_i 基礎上加上 4 個字節

g_i + 1 :由于 g_i 是一個指針變量, g_i 變量內存放的是地址,又因為 g_i 的值為 1 ,而 g_i + 1 就為 1 + 4 的單元的內存空間( 32 位機上),故 g_i + 1 5

轉自:http://blog.csdn.net/hxg130435477/archive/2009/03/21/4012686.aspx

總結

以上是生活随笔為你收集整理的extern数组与extern指针的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。