通过反汇编来理解restrict关键字
一次難忘的面試經歷
多年前,一次互聯網某廠實習生的面試題,題目的代碼片段很簡單,如下:
?1??#include?2??int?main()3?{4??????int?*restrict?pInt?=?(int*)malloc(4);5??????int?*pNewInt?=?pInt;6??????return?0;7?} 12345678面試官問我運行結果是什么,我居然當時不加思索的回答,編譯器會報錯。當時個人理解是這樣的,首先用malloc函數分配了一個4Bytes大小的內存,并讓pInt指針指向它。同時pInt指針,也被restrict關鍵字修飾。而restrict就是用來表明是訪問一個數據對象的唯一且初始的方式。因此在第5行中,將pInt的值賦值給了另一個指針pNewInt就是錯誤的。但是遺憾的是,這種錯誤編譯器是不會報錯的。下面我們就這個問題來聊一聊restrict關鍵字。
不使用restrict關鍵字
restrict關鍵字實際上在C99標準出來以前,編譯器就已經開始支持類似restrict的語句了,如編譯器定義了__restrict。restrict關鍵字,實際上是用來指示編譯器對代碼進行優化的。在分析具體含義以前,我們先來看一個沒有用restrict的例子。
在main函數中定義了三個參數p、q、r。這之后調用了f函數,并把p和q的地址傳遞進去。在f中根據這兩個地址,將p和q分別設置為2和3,最后把p的值2當做函數返回值返回,并賦值給整形變量r。這個代碼很簡單,不過重點不是看它,而是它的反匯編代碼,下面我們通過objdump工具執行objdump -j .text -l -C -S test來生成匯編代碼。?首先來看下main函數的匯編代碼。如下圖所示
在反匯編代碼中的可以看到,分別將p和q的地址分別賦值給了rdi和 rsi寄存器。即rdi保存了p的地址,rsi保存了q的地址。之后調用了f函數。而在f函數的反匯編代碼中,通過如下圖所示能確認rdi確實保存著p的地址
?接著,如下圖所示,可以看到通過調用了puts函數向屏幕打印信息
最后,f函數最后用rax所指向區域的值,賦給了eax寄存器,即將p的值放入eax作為返回值返回。
好了,現在有一個疑問,代碼其對應的反匯編代碼干嘛不直接把立即數2直接賦值給eax,反而多此一舉從rax所指向的內存中把2取出來給eax呢?因為編譯器之所以不直接把2賦值給eax,而非要從內存中獲取,就是擔心代碼中會通過其他指針訪問p所占的區域。在test.c的代碼中我們通過指針a訪問了p所占的區域,但是編譯器不知道指針b是否也是指向p所占的區域,通過指針b是否也對該區域做了修改。因此編譯器只能根據return *a,從內存中將2取出來賦值給eax。
使用restrict關鍵字
好了,怎么樣才能讓編譯器變得聰明一點呢?這個時候restrict就派上用場了。修改上面代碼
?運行編譯命令gcc -g -O2 -std=c99 -o test test.c進行編譯,使用反匯編工具objdump查看發現
可以看到直接把2賦給了eax寄存器,作為f的返回值返回了。看明白了吧,restrict關鍵字起作用了,編譯器檢測到指針a和b都使用了restrict關鍵字,也就是說,它們所指向的空間都只能通過指針a和b訪問,不會有其他途徑。這個時候,編譯器就可以放心大膽地進行優化了,直接把2賦給了eax,而不用再從內存中獲取了。這樣,f的執行效率也提高了。這里還有一點需要注意,C 程序并不支持restrict關鍵字,但是可以使用“__restrict”關鍵字。
總結
以上是生活随笔為你收集整理的通过反汇编来理解restrict关键字的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 剑网三按键精灵设置(剑网三按键精灵怎么设
- 下一篇: 直播背景音效助手电脑版(高级主播音效助手