在閱讀本文以前,請先閱讀gcc的相關文檔,確保對如何在c中使用匯編語言有個基本的認識。html
文檔地址爲:sass
https://gcc.gnu.org/onlinedoc...微信
下面用示例講解下相關概念:函數
#include <assert.h> #include <stdint.h> #include <stdio.h> void do_check(uint32_t dwSomeValue) { uint32_t dwRes; asm("bsfl %1,%0" : "=r"(dwRes) : "r"(dwSomeValue) : "cc"); assert(dwRes == 3); } int main(int argc, char *argv[]) { do_check(8); }
編譯後再以彙編形式查看do_check方法:性能
$ gcc -O3 main.c && objdump --disassemble=do_check a.out 0000000000001190 <do_check>: 1190: 0f bc ff bsf %edi,%edi 1193: 83 ff 03 cmp $0x3,%edi 1196: 75 01 jne 1199 <do_check+0x9> 1198: c3 retq 1199: 50 push %rax 119a: e8 c1 ff ff ff callq 1160 <do_check.part.0>
因爲assert宏中使用了asm語句中的輸出參數dwRes,因此即便asm語句沒有指定volatile,do_check方法也是在正常執行的。優化
下面看下把assert方法去掉以後的do_check彙編代碼:ui
$ gcc -O3 -D NDEBUG main.c && objdump --disassemble=do_check a.out 0000000000001130 <do_check>: 1130: c3 retq
由上可見,由於咱們在執行gcc時加了-D NDEBUG參數,定義了NDEBUG宏,因此上面的assert宏最終會變爲空指令。spa
也就是說,do_check方法中沒有任何地方在使用asm語句中的輸出參數dwRes,因此gcc就會在優化後的代碼中刪除掉該asm語句,因此上面的do_check方法最終變成了空方法。code
那咱們將上面的asm語句加上volatile再試下:htm
void do_check(uint32_t dwSomeValue) { uint32_t dwRes; asm volatile("bsfl %1,%0" : "=r"(dwRes) : "r"(dwSomeValue) : "cc"); assert(dwRes == 3); }
編譯後再以彙編形式查看do_check方法:
$ gcc -O3 -D NDEBUG main.c && objdump --disassemble=do_check a.out 0000000000001130 <do_check>: 1130: 0f bc ff bsf %edi,%edi 1133: c3 retq
由上能夠看到,此次即便指定了-D NDEBUG參數,使assert宏變成了空操做,致使do_check方法中沒有任何地方使用dwRes變量,但因爲volatile的存在,該asm語句仍是沒有被優化掉。
經過上面的例子,咱們就能夠看到 volatile 是如何防止 gcc 優化代碼的,可是在上面的例子中,該優化是一個正確的優化,因此不該該加 volatile。
若是有其餘的asm語句,雖然它的輸出參數沒有被使用,但也不該該被優化掉,這個時候就應該使用 volatile 了。
但願對你有所幫助。
完。
更多原創文章,請關注我微信公衆號: