C語言中「.」與「->」有什麼區別?


C#不就都統一了。

說來Pascal這個語法就設計的很好看。你看*p寫成p^,p.q寫成p.q,(*p).q就當然是p^.q了,省掉了什麼-&>這些亂七八糟的東西。


首先 a-&>b 的含義是 (*a).b ,所以他們是不同的,不過的確 -&> 可以用 * 和 . 實現,不需要單獨一個運算符。

嗯,我這是說現代的標準化的 C 語義上來說, -&> 可以用 * 和 . 的組合實現。

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

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

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

a = b

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

*A = *B

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

好,以上是基本背景。

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

假設我們有

struct Point {

int x;

int y;

};

struct Point p;

struct Point *pp = p;

從現代語義上講 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);

而以下代碼是錯的:

(*pp).x = 1;

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

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

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

pp-&>x = 1;

嗯,這就是它存在的歷史原因。

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

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


用「.」的話,只需要聲明一個結構體。格式是,結構體類型名+結構體名。然後用結構體名加「.」加域名就可以引用域 了。因為自動分配了結構體的內存。如同 int a;一樣。

用-&>的話,要聲明一個結構體的指針,還要手動開闢一個該結構體的內存,然後把返回的指針給聲明的結構體指針。才能用-&>正確引用。否則內存中只分配了指針的內存,沒有分配結構體的內存,想要的結構體實際上是不存在。這時候用-&>引用自然出錯了,因為沒有結構體,自然沒有結構體的域了。

"."我直接讀做"的」。

-&>我讀作"指向的結構體的"。


-&>告訴你這裡有個指針,要注意!


現在看是多此一舉。

補充:請參考stdc新聞組的討論:https://groups.google.com/forum/#!topic/comp.std.c/VZ-9nh9qrbU


用.的時候你給的是立即數地址,用-&>的時候你給的是指針

嗯當然a-&>b等價於(*a).b

不過還是前面這個省事

然而雖然編譯器這麼強大了,但是也不能自作主張把指針當成指針的,萬一你這裡的意思是做強制類型轉換呢


T* 和 T 是兩個完全不同的類型,T 類型可能有一個 foo 方法,可以用 obj.foo() 來調用,而 T* 實質就是一個 size_t 類型,它不具有任何方法,通過 * 運算符取得 referent,就可以調用它的成員方法了,這時表達式是 (*pObj).foo()。看起來比較麻煩,所以 -&> 運算符誕生,實質上就是個語法糖。


對於指針p p-&>a 被定義為 (*p).a (不成文的標準)


指針結構體變數用 -&>, 普通結構體變數用 .

假設有結構體

typedef struct {

int a;

int* b;

} mystruct;

mystruct t1;

mystruct *t2;

t2=t1;

其中,t1是一個內存的地址,這個內存的大小就是整個mystruct結構體的大小(64位系統下佔12位元組),內容就是結構體的內容 a和b.當然,由於是局部變數,所以是放到棧里的.但是你只需要知道,這個t1指向的內存佔用了12位元組.

t2是一個指針,它指向了t1所在的那個內存地址.t2佔用的內存只有8位元組(一個指針類型的大小).而內存的內容就是t1的內存地址.所以,對於t2.a來說是錯誤的,因為t2並不是一個結構體的地址,它並沒有成員a.

但是*t2就和t1是等價的,所以 (*t2).a == t1.a. t2-&>a就相當於(*t2).a.


.前面是結構體,-&>前面是結構體指針。


指針操作要比對象操作危險100倍。在實踐中-&>出core的概率要遠高於.。.出core的情況也在於在它之前有指針操作,如*。指針操作的危險的根本原因在於指針操作的絕大多數異常是在編譯時無法捕獲的。這也是c/c++與JAVA的本質區別。

-&>與*.是否等價的問題,理論上確實是等價的。但在實踐中有所區別,特別是在調core中。以空指針和野指針為例。-&>都會直接導致出core。而對於*.來說,空指針會在*操作出core,野指針會在.操作出core。

c/c++有2-3年實踐經驗才算入門,有5-6年經驗再來批判別人,不要紙上談兵。說語言不重要,重要的是編程思想的,都是搞java開發的。


假設 有一個

class human
{
public:
func();
}

.是給明確的 類 或者 結構體 的對象用的, 比如

human xiao_ming;
xiao_ming.func();

-&> 是給 類 或者 結構體 的指針用的, 比如

human * xiao_ming = new human;
xiao_ming -&> func();


.的是正常的對象,→是指針對象


. 就意味著前面的是一個結構體,-&>意味著是一個指針。可以方便閱讀。我覺得這很方便。


存的地方不同吧,用.的是在棧里的局部變數,局部變數在方法執行完後會清空棧,所以不用free垃圾回收,這時整修結構體都在棧中。而-&>是在棧中聲明了一個指針,指向堆中的結構體,這樣方法運行完後只會清空棧中的指針,所以需要手動free清空堆中的結構體。


既然是C語言中,那就用結構體舉例吧。聲明一個結構體,然後用其創建一個結構體變數,然後創建一個結構體類型指針。這時想要訪問結構體中的對象。一種方法是要用結構體變數加上「.」運算符;另一種方法是用指針變數加上「-&>」運算符


一樣。 -&>更直觀些。 就像x = x + 1; 也可以是 x += 1;沒什麼可糾結的。

工作四年多我慢慢發現,我們又不是開發語言語法的,作為使用者掌握編程思想才是王道,我現在感覺《數據結構》甚至可以當做哲學書來看……


"."的左操作數為值,"-&>"的左操作數為指針.題主的感覺是對的,"."和"-&>"實際是可以合併為一個操作符的,在很多新語言中對值和指針取成員已經統一為"."了,C中保留只是一種傳統的延續.


地址用-&>,變數用.,至於效率,可以去看反彙編~


.是結構體,操作棧內存,

-&>是指向結構體的指針,操作堆內存。

棧內存由系統分配,不需要手動free,

堆內存自己malloc,必須free。

通常實際使用中-&>很常用。


難道已經沒有人看《c程序設計語言》了嗎,如下第二版115面是答案。

還有二者等價我覺得不妥,指針就是個地址,給指針安排成員優點少缺點多,程序設計語言講究設計哲學和實現方法連貫一致,開特例不是好的解決問題的方法,殊不知字元串以結尾造成的損失啊。


唔~FC教授說的已經很全面了,不過還有個黑科技是-&>可被重載,而.不行

話說……so上不是有類似問題么https://stackoverflow.com/questions/1238613/what-is-the-difference-between-the-dot-operator-and-in-c


推薦閱讀:

導出音頻的時候如何做到音量最大化?
CMU 計算機選課上有什麼特別好的課程推薦?
二本學生如何獲得微軟的實習機會?
該不該放棄學了四個月的Haskell,轉去學C++?
c++成員函數聲明()後加&或&&表示什麼?

TAG:編程 | 計算機 | C編程語言 |