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

最新文章

  1. Erlang使用相关笔记
  2. C#GridViewExport帮助类,美化导出
  3. 线程学习笔记(EventWaitHandler)AutoResetEvent的使用
  4. java中接口与多重继承的关系
  5. 【python】Python 3 +pycharm中文支持解决方案
  6. HDOJ的题目分类
  7. 基于Java图片数据库Neo4j 3.0.0发布 全新的内部架构
  8. 用python做自己主动化測试--对server端的自己主动化測试(3)-很多其它http client实例
  9. compute post expression
  10. 框架 之 -------Spring
  11. Python实现正交实验法自动设计测试用例
  12. 微信小程序之自定义toast弹窗
  13. java日期操作常用工具
  14. 第二部分之AOF持久化(第十一章)
  15. ①泡茶看数据结构-表ADT
  16. jar包读取jar包内部和外部的配置文件,springboot读取外部配置文件的方法
  17. SD从零开始51-54 信用控制范围, 信用范围数据维护, 自动信用控制, 信用控制-阻止后续功能
  18. Spark GraphX图处理编程实例
  19. cydia源
  20. BTN事件的响应区域

热门文章

  1. hiho 1604 - 股票价格,思维题
  2. nginx的location 匹配的规则问题
  3. ActiveMQ学习笔记(6)----ActiveMQ整合Spring开发
  4. Matlab从入门到精通 Chapter5 数据可视化--
  5. django框架-Admin管理站点搭建
  6. ormlite 中的onUpgrade
  7. codeforces #313(div 2)
  8. 单点登录(二)使用Cookie+File实现单点登录登出(附源代码)
  9. hpuoj--校赛--爬楼梯(模拟)
  10. reverse(两种反向生成url django原生形式和rest_framework中版本的形式)