C語言編譯器是如何實現指針+1這樣的一個機制?

int *p = array;
p++;

則 p 指向array 的下一個元素。我想好奇的是編譯器怎麼實現這樣的機制?這種情況下,++操作是否仍然比 +1 速度快?


實現機制:

編譯期做靜態類型檢查,*p是int類型,int在多數架構上為四位元組,++和+1都被轉譯為+4。

效率:

http://gcc.godbolt.org/ ,無優化

結論:+1時沒有任何區別。


p的值被load到某個寄存器r,然後add r, 4; 就可以了。這裡4已經被編譯器用sizeof(array item type) 給換成立即數了。所以你說的什麼p++和+1,實際上沒什麼區別。如果有人要拿這個來說快慢,恕我直言,也許是因為他還不夠了解底層的cpu是如何執行指令的這一個層面。


這個很簡單,編譯器知道int的sizeof,你+1,編譯器直接翻譯成+1*4就行了.

至於效率,我記得之所以有++,是因為早期某一種cpu上有個自增指令,效率比+1要高,

所以多了個++.現在已經一致了. ++或者+1並不影響變數放在寄存器或者棧上.

x86下這種指針運算,常用lea指令,有:

lea eax,[eax+4] ; 假設eax是int指針,這是+1的意思.

對lea指令來說,這裡+4還是+1,執行效率是沒有區別的.

然後,如果不是p++,而是p+=i呢? 會不會多出一條乘法指令影響效率? 不會的,x86設計時已經

充分考慮了各種可能的指針運算,在處理指針加乘時,lea能這樣:

lea eax,[eax+ecx*4]

這條指令可以在一條指令里完成加法和乘法,效率和

lea eax,[eax+ecx]

沒有任何區別.

即使不用lea,其他指令也可以實現,例如:

movaps xmm1,[eax+ecx*4]

效率和

movaps xmm1,[eax+ecx]

完全一樣.


「++」操作是歷史的遺留。因為在早期 PDP 機器上專門有一個 register,每次被讀的時候內容自動加一。所以 C 就「順勢」實現了這個操作。

「+1」操作無限制,等效即可。在有些循環中甚至會被 auto-vectorizing 成為 SSE/SSE2 的單條指令。


指針有類型,類型有尺寸,指針+1就是當前值加上指針所指類型的尺寸,數組的元素在內存中是連續存放的,指向一個元素的指針加上一個元素的尺寸自然就是下一個元素。

從原理上說++應該會慢一點,以x86為例,++需要將一個變數加1再寫回內存,+1是將一個變數加1並將結果保持在寄存器AX/EAX中,但是:1、編譯器會作優化,不做無謂的內存回寫;2、內存有cache,最靠近CPU的cache速度接近寄存器;3、一些RISC,例如SPARC上所有算術操作都是在寄存器窗口中進行的。綜上,其實兩個操作速度差不多。


C語言那個編譯器裡面,已經存放了變數類型,它會自己計算長度的。我們寫指針加幾,比如n好了,它都會自動換算成類型長度*n的。類型名就是修飾它跳躍的方式的。

你也可以反過來想,如果不是這樣,指針怎麼知道它要一步跳多遠呢?


反彙編一下C編譯的程序即可,比如用obj-dump 這個工具,我的理解編譯根據CPU指令不同,採用不同機制。


int *p = array;
p++;

int *p=array;處理為LEA p, array; array必須為靜態地址,編譯時確定好的

p++處理為((char *)p) += sizeof(*p);這樣了,不會處理為INC p; 這樣


推薦閱讀:

為什麼很多人建議學C語言不用任何IDE,直接用編輯器和編譯器?
C 語言有哪些缺陷?
int i=0,j; for(j=3;i=j=0;i++,j++)循環多少次?
請問培訓,選擇python還是unity3d?
如何理解 C 語言中的 typedef ?

TAG:C編程語言 | 編譯原理 | 指針 |