經常看到有人介紹一些IDE或者像gdb這樣的調試器的很高級的調試功能,也聽人說過有些牛人做工程的時候就用printf來調試,不用特殊的調試器。特別是在代碼經過編譯器一些比較復雜的優化后,會變得“難以辨認”,使用調試器也變得有些頭疼。先舉個簡單的例子:
1 #include <stdio.h>
2 3 int main(){ 4 int a[6], i, sum = 0; 5 for(i = 0; i<6; i++) 6 a[i] = i<<2; 7 a[3] = 5; 8 for(i = 0; i<6; i++) 9 sum += a[i]; 10 printf("sum = %d\n", sum); 11 return 0; 12 }
如果采用gcc(筆者的版本是4.7.3)編譯,使用
1 gcc -O3 sum.c -S
來編譯,可以查看到編譯出來的匯編代碼是:
1 .file "sum.c" 2 .section .rodata.str1.1,"aMS",@progbits,1 3 .LC0: 4 .string "sum = %d\n" 5 .section .text.startup,"ax",@progbits 6 .p2align 4,,15 7 .globl main 8 .type main, @function 9 main: 10 .LFB24: 11 .cfi_startproc 12 subq $40, %rsp 13 .cfi_def_cfa_offset 48 14 movl $53, %edx 15 movl $.LC0, %esi 16 movl $1, %edi 17 xorl %eax, %eax 18 call __printf_chk 19 xorl %eax, %eax 20 addq $40, %rsp 21 .cfi_def_cfa_offset 8 22 ret 23 .cfi_endproc 24 .LFE24: 25 .size main, .-main 26 .ident "GCC: (Ubuntu/Linaro 4.7.3-1ubuntu1) 4.7.3" 27 .section .note.GNU-stack,"",@progbits
說白了,就是gcc直接將main()優化成了這樣:
1 int main(){ 2 printf("sum = %d\n", 53); 3 return 0; 4 }
可想而知,對於這樣優化的代碼,調試器也會抓狂。
那么采用printf大法的好處就出來了,無論編譯器如何優化,printf的輸出總是正確的(編譯器的優化總是保證程序效果不變),而且相較於調試器各種高深摸測的命令,printf的用法是程序猿的必備知識,所以利用printf來跟蹤程序有的時候比調試器還要方便。雖然有的時候printf可能顯得不那么安全,但你可換其它的安全的輸出函數啊。其實printf大法的實質就是輸出大法,直接在程序(當然是debug版的,或者說verbose功能下,release版當然…你懂的…)運行的時候屏顯各種希望獲取的運行時信息。
如何printf一個變量的值,我就不多說了,畢竟這是咱們程序猿的基本功。我是想要介紹一些調試用的宏:
宏名(每個宏名前后雙下划線) | 類型 | 意義 |
__FILE__ | 字符串 | 當前程序名 |
__FUNCTION__ | 字符串 | 當前函數名 |
__LINE__ | 整數 | 當前行號(在源代碼中的) |
__DATE__ | 字符串 | 被編譯的日期 |
__TIME__ | 字符串 | 被編譯的時間 |
__STDC__ | 整數(布爾) | 如果編譯器按照ANSI C來編譯,為非零值;否則為0 |
使用這些宏來配合printf,可以做到很好的調試(當然也可以去做條件編譯,不過本文暫不討論這方面的應用)。
比如我可以定義一個BUG()如下:
1 #define BUG() printf("Bug in function: %s (file: %s), @line: %d. It is compiled on %s %s, %s ANSI C standard.\n", __FUNCTION__, __FILE__, __LINE__, __TIME__, __DATE__, __STDC__? "with" : "without");
當我覺得可能是對某函數因為參數指針p是NULL而使得程序崩潰,那么我可以在該操作中加入如下一句:
1 if(!p) 2 BUG();
這樣如果真的因為p是NULL造成的程序崩潰的話,程序退出前會輸出這個BUG在源代碼中的位置,方便我們追蹤它。至於為什么要輸出編譯的時間和日期,有的時候我們修改了.h文件,而往往在Makefile中我們是不寫.h的依賴關系的,這樣就可能會造成某種不一致,這時候輸出代碼編譯的時間、日期就顯得很有用了。最后那個ANSI C的檢查,其實只是個以防萬一而已。
轉自:https://www.cnblogs.com/Leo_wl/p/3251234.html