1. 數(shù)組并非指針
注意以下聲明的區(qū)別:
extern int *x; //聲明 x 是個int型的指針;
extern int y[]; //聲明 y 是個int型數(shù)組,長度尚未確定,其存儲在別處定義。
錯誤示例:
文件1:
int mango[100];
文件2:
extern int *mango; //正確形式: extern int mango[];
…………
/*一些引用mango[ i ]的代碼*/
補(bǔ)充:
C語言中的對象必須有且只有一個定義,但它可以有多個extern聲明。
定義:只能出現(xiàn)在一個地方; 確定對象的類型并分配內(nèi)存,用于創(chuàng)建新的對象。如:int my_array[100];
聲明:可以多次出現(xiàn); 描述對象的類型,用于指代其他地方定義的對象。如:extern int my_array[];
由于extern對象聲明告訴編譯器對象的類型和名字,對象的內(nèi)存分配則在別處進(jìn)行。由于并未在聲明中位數(shù)組分配內(nèi)存,所以并不需要提供關(guān)于數(shù)組長度的信息。對于多維數(shù)組,需要提供最左邊一維最外的其他維德長度。所以,extern int my_array[] 與 extern int my_array[100]等價(jià)。 |
2. 指針和數(shù)組是如何訪問的
首先要區(qū)分“地址y” 和“地址y的內(nèi)容”之間的區(qū)別,如 x = y;
在這個上下文環(huán)境里,符號 x 的含義是 x 所代表的地址。被稱為左值。左值在編譯時(shí)可知,左值表示存儲結(jié)果的地方。
符號 y 的含義是 y 所代表的地址的內(nèi)容。這被稱為右值。右值直到運(yùn)行時(shí)才知。如無特別說明,右值表示“y的內(nèi)容”。
注:C語言引入了“可修改的左值”這個術(shù)語。它表示左值允許出現(xiàn)這賦值語句的左邊。這個奇怪的術(shù)語是為了和數(shù)組名區(qū)分,駐足名也用于確定對象在內(nèi)存中的位置,也是左值,但它不能作為賦值的對象。因此,數(shù)組名是個左值但不是可修改的左值。 標(biāo)準(zhǔn)規(guī)定賦值符必須用可修改的左值作為它左邊一側(cè)的操作數(shù)。用通俗的話講:只能給可以修改的東西賦值。
編譯器為每個變量分配一個地址(左值),這個地址在編譯時(shí)可知,而且該變量在運(yùn)行時(shí)一直保存于這個地址。相反,存儲于變量中的值(它的右值)只有在運(yùn)行時(shí)才可知。如果需要用到變量中存儲的值,編譯器就發(fā)出指令從指定地址讀入變量值并將它存于寄存器中。
這里的關(guān)鍵之處在于:每個符號的地址在編譯時(shí)可知。所以,如果編譯器需要一個地址(可能還需要加上偏移量)來執(zhí)行某種操作,它就可以直接進(jìn)行操作,并不需要增加指令首先取得具體的地址。相反,對于指針,必須首先在運(yùn)行時(shí)取得它的當(dāng)前值,然后才能對它進(jìn)行解除引用操作(作為以后進(jìn)行查找的步驟之一)。下圖展示了對數(shù)組下標(biāo)的引用。

上圖同時(shí)也說明為什么extern char a[]與extern char a[100]等價(jià)。這兩個聲明都指明 a 是一個數(shù)組,也就是一個內(nèi)存地址。編譯器并不知道數(shù)組有多長,因?yàn)樗划a(chǎn)生偏離其實(shí)地址的偏移地址。從數(shù)組提取一個字符,只要簡單地從符號表顯示的 a 的地址加上下標(biāo),需要的字符就位于這個地址中。
相反,如果聲明 extern char *p,它告訴編譯器 p 是一個指針(在許多現(xiàn)代的機(jī)器里它是四字節(jié)的對象),它指向的對象是一個字符。為了取得這個字符,必須得到地址 p 的內(nèi)容,把它作為字符的地址并從這個地址中取得這個字符。指針的訪問要靈活的多,但需要增加一次額外的提取,如下圖所示。

3. 當(dāng)你“定義為指針,但以數(shù)組方式引用”時(shí)會發(fā)生什么
當(dāng)一個外部數(shù)組的實(shí)際定義是一個指針,但卻以數(shù)組方式引用時(shí),會引起什么問題。(本來針對數(shù)組)需要對內(nèi)存進(jìn)行直接的引用(如第一個圖所示),但是這時(shí)編譯器所執(zhí)行的確是對內(nèi)存進(jìn)行間接引用(如上圖)。之所以會這樣,是因?yàn)槲覀兏嬖V編譯器我們擁有的是一個指針,如下圖所示。

4. 數(shù)組和指針的其他區(qū)別
數(shù)組和指針都可以在他們的定義中用字符串常量進(jìn)行初始化。盡管看上去一樣,底層的機(jī)制卻不相同。
定義指針時(shí),編譯器并不為指針?biāo)赶虻膶ο蠓峙淇臻g,它只是分配指針本身的空間,除非在定義時(shí)同時(shí)賦給指針一個字符串常量進(jìn)行初始化。例如,下面的定義創(chuàng)建了一個字符串常量(為其分配了內(nèi)存):
char *p = “breadfruit”;
注意:只有對字符串常量才是如此。不能指望為浮點(diǎn)數(shù)之類的常量分配空間,如:
float *pip = 3.14; //錯誤!無法通過編譯
注:在ANSI C中,初始化指針?biāo)鶆?chuàng)建的字符串常量被定義為只讀。如果試圖通過指針修改這個字符串常量,程序會出現(xiàn)未定義的行為。
與指針相反,由字符串常量初始化的數(shù)組是可以修改的。其中的單個字符在以后可以改變,比如下面語句:
strncpy( a, “black”, 5 );
就將數(shù)組的值修改為 “blackberry”。
|