深入探索 C/C++ 陣列與指標的奧祕之二:陣列名是一個指標常量嗎?

iteye_6233發表於2011-03-19

深入探索 C/C++ 陣列與指標的奧祕之二:陣列名是一個指標常量嗎?

陣列名是一個指標常量這種觀點來源於陣列名在表示式計算中與指標的結果等效性。例如下面的程式碼:

int a[10], *p = a, *q; q = a + 1; q = p + 1;
在效果上看,a + 1 與 p + 1 是相同的,這很容易給人一種 a 就是 p 的假象,但,這僅僅是假象。鑑於指標常量包含了指標和常量兩類概念,我們可以把這個問題分開兩部分進行討論。
一、陣列名是指標嗎?
在《C 與指標》一書中,作者用一個著名的例子闡述了陣列名與指標的不同。在一個檔案中定義:int a[10];然後在另一個檔案中宣告:extern int *a; 筆者不在這裡重複其中的原理,書中的作者試圖從底層操作上闡述陣列名與指標的不同點,但筆者認為這個例子存在一些不足,a 在表示式中會轉換為一個非物件的符號地址,而指標 a 卻是一個物件,用一個非物件去跟一個物件比較,有“偷跑”的嫌疑,這個例子只是說明了陣列名的非物件性質,只能證明物件與非物件實體在底層操作上的不同,事實上,如上一章所述,指標也有非物件形態。筆者認為,無須從底層的角度上花費那麼多脣舌,僅僅從字面上的語義就可以推翻陣列名是一個指標的觀點。
首先,在 C/C++ 中,陣列型別跟指標型別是兩種不同的派生型別,陣列名跟指標是兩種不同型別的實體,把陣列型別的實體說成“是”另一個型別的實體,本身就是荒謬的;
其次,a + 1 在效果上之所以等同於 p + 1,是因為 a 進行了陣列到指標的隱式轉換,這是一個轉換的過程,是 converted to 而不是 is a 的過程。如果是兩個相同的事物,又怎會有轉換的過程呢?當把 a 放在 a + 1 表示式中時,a 已經從一個陣列名轉換為一個指標,a 是作為指標而不是陣列名參與運算的;
第三,a + 1 與 p + 1 是等效關係,不是等價關係。等價是相同事物的不同表現形式,而等效是不同事物的相同效果。把陣列名說成是指標實際上把等效關係誤解為等價關係。
因此,陣列名不是指標,永遠也不是,但在一定條件下,陣列名可以轉換為指標。
二、陣列名是一個常量嗎?
看見這句話有人會覺得奇怪,陣列定義之後就不能改變了,陣列名不就是個常量嗎?在表示式中,陣列名的確可以轉換為一個不變的符號地址,但在C 中,不變的實體不一定是常量!而且,C/C++ 有常量與常量表示式之分,常量與常量表示式是兩種不同的實體,但常量表示式可以作為常量使用。C/C++ 中的常量雖然有所不同,但都不包括陣列或陣列名,而且陣列名也不一定是常量表示式。
請在 C90 的編譯器中編譯如下程式碼,注意不能是 C99 和 C++ 的,因為 C99 和 C++ 不再規定陣列的初始化器必須是常量表示式,會看不到效果:

int main( void ) { static int a[10], b[10]; int c[10], d[10]; int* e[] = { a, b }; /* A */ int* f[] = { c, d }; /* B */ return 0; }
B 為什麼不能通過編譯?是由於自動陣列名並不是常量表示式。在 C 中,常量表示式必須是編譯期的,只在執行期不變的實體不是常量表示式,請看標準的摘錄:
6.6 Constant expressions
A constant expression can be evaluated during translation rather than runtime, and accordingly may be used in any place that a constant may be.
c 和 d 是自動陣列,首地址在編譯期是不可知的,因為這樣的物件在編譯期還不存在;a 和 b 是靜態陣列,靜態物件從程式開始時就已存在,因此 a 和 b 的首地址在編譯期是已知的,它們都屬於常量表示式中的地址常量表示式。
所以,C/C++ 中的陣列名,都不是常量。C 中的陣列名,是否常量表示式要視其儲存連續性而定,全域性陣列、靜態陣列名都是常量表示式,而自動陣列名不是。在 C++ 中,由於不再規定常量表示式必須是編譯期的,因此 C++ 的陣列名都是常量表示式。
原文連結:http://blog.csdn.net/supermegaboy/archive/2009/11/23/4855018.aspx

相關文章