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
C语言调试小技巧的更多相关文章
- C语言中的调试小技巧
C语言中的调试小技巧 经常看到有人介绍一些IDE或者像gdb这样的调试器的很高级的调试功能,也听人说过有些牛人做工程的时候就用printf来调试,不用特殊的调试器.特别是在代码经过编译器一些比较复杂的 ...
- 嵌入式C语言优化小技巧
嵌入式C语言优化小技巧 1 概述 嵌入式系统是指完成一种或几种特定功能的计算机系统,具有自动化程度高,响应速度快等优点,目前已广泛应用于消费电子,工业控制等领域.嵌入式系统受其使用的硬件以及运行环境的 ...
- 微信移动端web页面调试小技巧
技术贴还是分享出来更加好,希望能对一些朋友有帮助,个人博客 http://lizhug.com/mymajor/微信移动端web页面调试小技巧
- 通过Dapr实现一个简单的基于.net的微服务电商系统(十四)——开发环境容器调试小技巧
之前有很多同学提到如何做容器调试,特别是k8s环境下的容器调试,今天就讲讲我是如何调试的.大家都知道在vs自带的创建项目模板里勾选docker即可通过F5启动docker容器调试.但是对于启动在k8s ...
- iOS开发中调试小技巧
对于软件开发而言,调试是必须学会的技能,重要性不言而喻.对于调试的技能,基本上是可以迁移的,也就是说你以前在其他平台上掌握的很多调试技巧,很多也是可以用在iOS开发中.不同语言.不同IDE.不同平台的 ...
- iOS - 开发中调试小技巧
对于软件开发而言,调试是必须学会的技能,重要性不言而喻.对于调试的技能,基本上是可以迁移的,也就是说你以前在其他平台上掌握的很多调试技巧,很多也是可以用在iOS开发中.不同语言.不同IDE.不同平台的 ...
- 11个强大的Visual Studio调试小技巧
简介 调试是软件开发周期中很重要的一部分.它具有挑战性,同时也很让人疑惑和烦恼.总的来说,对于稍大一点的程序,调试是不可避免的.最近几年,调试工具的发展让很多调试任务变的越来越简单和省时. 这篇文章总 ...
- 11个强大的Visual Studio调试小技巧(转)
简介 调试是软件开发周期中很重要的一部分.它具有挑战性,同时也很让人疑惑和烦恼.总的来说,对于稍大一点的程序,调试是不可避免的.最近几年,调试工具的发展让很多调试任务变的越来越简单和省时. 这篇文章总 ...
- MEF依赖注入调试小技巧!
自从哥的项目使用MEF以来,天天那个纠结啊,甭提了.稍有错误,MEF就报错,但就不告诉你哪错了,大爷的. 后来看了MEFX的相关调试方法,感觉也不太理想,根本不够直观的看到错误原因,也许是没有深入学习 ...
随机推荐
- POJ 3723 Conscription (Kruskal并查集求最小生成树)
Conscription Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 14661 Accepted: 5102 Des ...
- hiho152周 - 水题 区间问题
题目链接 给定两个区间集合 A 和 B,其中集合 A 包含 N 个区间[ A1, A2 ], [ A3, A4 ], ..., [ A2N-1, A2N ],集合 B 包含 M 个区间[ B1, B2 ...
- 路飞学城-Python开发-第二章
''' 数据结构: menu = { '北京':{ '海淀':{ '五道口':{ 'soho':{}, '网易':{}, 'google':{} }, '中关村':{ '爱奇艺':{}, '汽车之家' ...
- ORACLE查询优化之is null和is not null优化
最近工作的时候遇到了比较大的数据查询,自己的sql在数据量小的时候没问题,在数据量达到300W的时候特别慢,只有自己优化sql了,以前没有优化过,所以记录下来自己的优化过程,本次是关于is null和 ...
- LOJ #10121 与众不同 (RMQ+二分)
题目大意 :给你一个整数序列,定义一个合法子串为子串内所有数互不相同,会有很多询问,求区间$[L,R]$内最长连续合法子串长度 一道思维不错的$RMQ$题,NOIP要是考这种题可能会考挂一片 预处理出 ...
- ip iproute2的典型应用
net-tools和iproute2的命令做对比,做到简单明了,分别演示如何去获取.配置和操作系统网络信息. 以下是net-tools和iproute2的大致对比: 4.1 ip link set-- ...
- win10 64位下VirtualBox安装CentOS6.9
第一步:安装VritualBox 百度“VritualBox”下载安装即可: 第二步:下载Linux镜像系统并安装 这里写出我参照的博客,很详细,我就不累赘了! 原文地址:http://blog.cs ...
- #error 、 #line 和 #pragma 的使用
1. #error 的用法 (1)#error 是一种预编译器指示字,用于生成一个编译错误消息 (2)用法:#error message //注意:message 不需要用双引号包围 (3)#erro ...
- POJ——T 1469 COURSES
http://poj.org/problem?id=1469 Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 24197 ...
- 如何成为一个偷懒又高效的Android开发人员
我敢肯定你对这个标题肯定心存疑惑,但事实就是如此,这个标题完全适合Android开发人员.据我所知, Android程序员不情愿写 findViewById().点击事件监听等重复率较高的代码.那我们 ...