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

C语言调试小技巧的更多相关文章

  1. C语言中的调试小技巧

    C语言中的调试小技巧 经常看到有人介绍一些IDE或者像gdb这样的调试器的很高级的调试功能,也听人说过有些牛人做工程的时候就用printf来调试,不用特殊的调试器.特别是在代码经过编译器一些比较复杂的 ...

  2. 嵌入式C语言优化小技巧

    嵌入式C语言优化小技巧 1 概述 嵌入式系统是指完成一种或几种特定功能的计算机系统,具有自动化程度高,响应速度快等优点,目前已广泛应用于消费电子,工业控制等领域.嵌入式系统受其使用的硬件以及运行环境的 ...

  3. 微信移动端web页面调试小技巧

    技术贴还是分享出来更加好,希望能对一些朋友有帮助,个人博客  http://lizhug.com/mymajor/微信移动端web页面调试小技巧

  4. 通过Dapr实现一个简单的基于.net的微服务电商系统(十四)——开发环境容器调试小技巧

    之前有很多同学提到如何做容器调试,特别是k8s环境下的容器调试,今天就讲讲我是如何调试的.大家都知道在vs自带的创建项目模板里勾选docker即可通过F5启动docker容器调试.但是对于启动在k8s ...

  5. iOS开发中调试小技巧

    对于软件开发而言,调试是必须学会的技能,重要性不言而喻.对于调试的技能,基本上是可以迁移的,也就是说你以前在其他平台上掌握的很多调试技巧,很多也是可以用在iOS开发中.不同语言.不同IDE.不同平台的 ...

  6. iOS - 开发中调试小技巧

    对于软件开发而言,调试是必须学会的技能,重要性不言而喻.对于调试的技能,基本上是可以迁移的,也就是说你以前在其他平台上掌握的很多调试技巧,很多也是可以用在iOS开发中.不同语言.不同IDE.不同平台的 ...

  7. 11个强大的Visual Studio调试小技巧

    简介 调试是软件开发周期中很重要的一部分.它具有挑战性,同时也很让人疑惑和烦恼.总的来说,对于稍大一点的程序,调试是不可避免的.最近几年,调试工具的发展让很多调试任务变的越来越简单和省时. 这篇文章总 ...

  8. 11个强大的Visual Studio调试小技巧(转)

    简介 调试是软件开发周期中很重要的一部分.它具有挑战性,同时也很让人疑惑和烦恼.总的来说,对于稍大一点的程序,调试是不可避免的.最近几年,调试工具的发展让很多调试任务变的越来越简单和省时. 这篇文章总 ...

  9. MEF依赖注入调试小技巧!

    自从哥的项目使用MEF以来,天天那个纠结啊,甭提了.稍有错误,MEF就报错,但就不告诉你哪错了,大爷的. 后来看了MEFX的相关调试方法,感觉也不太理想,根本不够直观的看到错误原因,也许是没有深入学习 ...

随机推荐

  1. Analysis of the Facebook.app for iOS

    Analysis of the Facebook.app for iOS Posted Oct 18, 2016 Did you ever wonder why the Facebook.app fo ...

  2. 优动漫PAINT-凌霄花画法

    再见小清新~这次教程教授的是凌霄花的画法!话说这个作者的花卉系列都很米粒啊~配色什么的,赞到没话说~ 教程是简单,呃.... 没有优动漫PAINT软件肿么办? 别着急,╭(╯^╰)╮ 小编给你送来了 ...

  3. 叁、js中的css

    一.子选择器:用来选择一个父元素直接的子元素,不包括子元素的子元素,它的符号为“>” 注:ie6不支持子代选择器. <!DOCTYPE html PUBLIC "-//W3C// ...

  4. django 获得请求头

    django 获得到的请求头封装在 request 的 META 中,为一个 dict 以下选自官方文档: https://docs.djangoproject.com/zh-hans/2.0/ref ...

  5. Ubuntu 如何进入系统文件/etc/profile修改内容

    转载:https://blog.csdn.net/cfq1491/article/details/81088117 /etc/profile 默认权限为 -rw-r--r-- 即只有root用户可以修 ...

  6. Cannot set web app root system property when WAR file is not expanded

    Cannot set web app root system property when WAR file is not expanded 在tomcat下面可以,在weblogic下面不行的处理方法 ...

  7. js模拟支付宝提交表单

    弄过支付宝的程序猿可能都知道,里面有非常多地方都用到了自提交表单的方式,支付宝的接口通过请求API的形式取得server返回的表单字符串,使用out.print("表单字符串")在 ...

  8. Ubuntu下用glade和GTK+开发C语言界面程序(一)

    前言:对于大学中计算机系的每年暑假的课设有太多想说的,能从中学到非常多东西,当然不排除打酱油的,这些能够掠过哦,凡事都打酱油.人生也是打酱油的吧. 2333. 对于大三曾经的课设一般的要求都是用C写的 ...

  9. Double Vision (Unity 5.0)

    Double Vision (Unity 5.0): 根据 http://http.developer.nvidia.com/CgTutorial/cg_tutorial_chapter03.html ...

  10. thinkphp5.0的验证码安装和相关错误

    thinkphp5.0的验证码安装和相关错误 问题 只要是之前使用thinkphp5框架搭建网站的时候发现不管如何调用验证码都无法使用,按照官网要求,使用composer安装验证码出现报错Fatal ...