※ 引述《purpose (purpose)》之銘言:
→ Feis: 從標準的角度來看, 這些講法不盡正確. C 跟 C++ 也不盡相同 03/22 21:14
→ jono103: 感謝p大回覆 你的說法可以接受 但F大又說不一定正確@@? 03/23 00:10
→ jono103: 可以請F大賜教嗎? 或給個方向或哪些書 非常感激 03/23 00:12
其實我也不太懂,尤其是對於 C++。不過就 C 的部份我可以稍微分享一下個人的理解。
int a[3];
就陣列來說,陣列名稱本身的型別是陣列 (如上面宣告的 a 其型別是 "長度為 3 的
int 陣列")。
在 C 標準裡面的規則是在運算時,除非做為 sizeof、取址 (&) 或 _AlignOf (C11)
的運算元,或陣列本身為 register storage,其他運算一律被轉為指向陣列第一個元素
的指標 (a 會轉為 &a[0])。而這個指標不是一個左值 (換句話說是無法被賦值 (=) 的)
依照這個規則,造成我們常理解成:在 C 裡面,一般情況下,陣列名稱就是當作指向第
一個陣列元素的指標在 "用"。甚至因為它不是一個左值,有些人會說它是個常數。
而這邊所指的一般情況甚至包含了我們最常用的陣列元素存取。
例如 a[2] 這個陣列元素存取的運算實際上會被轉為 (&a[0])[2],而 (&a[0])[2] 會用
*(&a[0] + 2) 去解釋。雖然就結果而言我們就是得到了 a 陣列的第三個元素,但是
就標準來說我們是透過指標運算去存取的。
這種種現象可能就是造成為什麼書裡面總是把陣列跟指標弄得一團亂的原因。
那函數呢 ?
void f();
C 標準裡對於函數的企圖在某些層面上跟陣列有點像,但是不幸的是函數本身更複雜。
就函數來說,函數名稱本身的型別是函數 (如上面宣告的 f 其型別就是回傳 void 型態
的函數)。
在 C 標準裡面的規則是在運算時,除非做為 sizeof、取址 (&) 或 _AlignOf(C11)
的運算元,其他運算一律轉為指向函數的指標。而這個指標不是一個左值。
你看看,這不是跟陣列很像嗎?
依照這個規則,造成我們常理解成:在 C 裡面,一般情況下,函數名稱就是當作指向
函數的指標在 "用"。甚至因為它不是一個左值,有些人會說它是個常數。
但是為甚麼上面這句聽起來怪怪的,如果我們呼叫 f() 會發生甚麼事?
就標準而言就是會把 f 轉型成 &f 然後呼叫指標所指向的函數。也就是說 f() 會變成
(&f)() 然後去呼叫 &f 所指向的函數 (阿不就是 f)。
換句話說,就 C 來說,存取陣列元素 ([]) 跟呼叫函數 (()) 概念上都是透過指標。
但是指向的東西本質上卻有很大的不同。
這兩者的差別最容易理解的應該就是 sizeof,因為不論陣列或函數名稱都不會對他自動
轉型,所以我們可以用來分辨其本質的不同。
sizeof(a) 是求陣列 a 所佔據的空間大小,其結果就是整個陣列所佔據的大小,相當和
藹可親。
那 sizeof(f) 呢?
C 標準跟我們說,"不可問、不可說"。對,這是個謎,而你不需要知道。
而陣列跟函數的指標運算還有一個很明顯的不同:
*a 會轉成 *(&a[0]) 去存取 a 陣列第一個元素
那 *f 呢?
照剛剛的規則會變成 *(&f) 然後變成 f,接著又會套用一次同樣的規則,又變成 &f
所以就運算上 *a 跟 a 型態就已經不同,但是 *f == f 而且 *f == &f
更莫名的就是 f == **f 還有 f == *********f
至於 C++ 的話, 我只能跟你說兩者在設計的哲學上就已經不一樣. 但是我不懂 C++ 就
不好說甚麼