【轉】C語言中“.”與“->”有什么區別?(詳解)


C語言中“.”與“->”有什么區別?

轉載:https://farseerfc.me/zhs/dot-and-arrow-in-c.html

原問題:C語言中“.”與“->”有什么區別?

除了表達形式有些不同,功能可以說完全一樣阿。那為何又要構造兩個功能一樣的運算符? 效率有差異?可是現在編譯器優化都那么強了,如果真是這樣豈不是有些多此一舉

剛剛翻了下書,說早期的C實現無法用結構直接當作參數在函數間傳遞,只能用指向結構的指針在函數間進行傳遞! 我想這應該也是最直觀的原因吧。

原作者回答:

首先 a->b 的含義是 (*a).b ,所以他們是不同的,不過的確-> 可以用 *.實現,不需要單獨一個運算符。 嗯,我這是說現代的標准化的 C 語義上來說, -> 可以用 * 和 . 的組合實現。

早期的 C 有一段時間的語義和現代的 C 的語義不太一樣。

稍微有點匯編的基礎的同學可能知道,在機器碼和匯編的角度來看,不存在變量,不存在 struct 這種東西,只存在寄存器和一個叫做內存的大數組。

所以變量,是 C 對內存地址的一個抽象,它代表了一個位置。舉個例子,C 里面我們寫:

a = b
  • 1

其實在匯編的角度來看更像是

*A = *B
  • 1

其中 A 和 B 各是兩個內存地址,是指針。

好,以上是基本背景。

基於這個背景我們討論一下 struct 是什么,以及 struct 的成員是什么。 假設我們有

struct Point {
        int x;
        int y;
};
struct Point p;
struct Point *pp = &p;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

從現代語義上講 p 就是一個結構體對象, x 和 y 各是其成員,嗯。

從匯編的語義上講, p 是一個不完整的地址,或者說,半個地址,再或者說,一個指向的東西是虛構出來的地址。而 x 和 y 各是在 Point 結構中的地址偏移量。也就是說,必須有 p 和 x 或者 p 和 y 同時出現,才形成一個完整的地址,單獨的一個 p 沒有意義。

早期的 C 就是在這樣的模型上建立的。所以對早期的 C 而言, *pp 沒有意義,你取得了一個 struct ,而這個 struct 不能塞在任何一個寄存器里,編譯器和 CPU 都無法表達這個東西。

這時候只有 p.x 和 p.y 有意義,它們有真實的地址。

早期的 C 就是這樣一個看起來怪異的語義,而它更貼近機器的表達。 所以對早期的 C 而言,以下的代碼是對的:

p.x = 1;
int *a;
a = &(p.x);
  • 1
  • 2
  • 3

而以下代碼是錯的:

(*pp).x = 1;
  • 1

因為作為這個賦值的目標地址表達式的一部分, *pp ,這個中間結果沒法直譯到機器碼。

所以對早期的 C 而言,對 pp 解引用的操作,必須和取成員的偏移的操作,這兩者緊密結合起來變成一個單獨的操作,其結果才有意義。

所以早期的 C 就發明了 -> ,表示這兩個操作緊密結合的操作。於是才能寫:

pp->x = 1;
  • 1

嗯,這就是它存在的歷史原因。 而這個歷史原因現在已經不重要了,現代的符合標准的 C 編譯器都知道 (*pp).x 和 pp->x 是等價的了。

說句題外話, C++ 里面還發明了 .* 和 ->* 這兩個運算符(注意 ->* 不是單獨的 -> 和 * 並排放的意思),關於為什么要發明這兩個運算符,而不能直接說 a ->* b 的意思就是 a ->(*b) ,這個就作為課堂作業吧。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM