C語言調試小技巧


經常看到有人介紹一些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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM