读《 Linux_C 编程一站式学习
可见在这里预处理器做了两件事情,一是把头文件 stdio.h stdlib.h 在代码中展开,二是把 #define 定义的标识符 N 替换成它的定义 20 (在代码中做了三处替换,分别位于数组的定义中和两个函数中)。像 #include #define 这种以 # 号开头的行称为预处理指示( PreprocessingDirective ),我们将在第 21
预处理学习其它预处理指示。此外,用 cpp main.c 命令也可以达到同样的效果,只做预处理而不编译, cpp 表示 C preprocessor
 
 
5. indent 工具
indent 工具可以把代码格式化成某种风格,例如把例 9.1 “ 缺少缩进和空白的代码 格式化成内核编码风格:
$ indent -kr -i8 main.c
$ cat main.c
 
-kr 选项表示 K&R 风格, -i8 表示缩进 8 个空格的长度。如果没有指定 -nut 选项,则每 8 个缩进空格会自动用一个 Tab 代替。注意 indent 命令会直接修改原文件,而不是打印到屏幕上或者输出到另一个文件,这一点和很多 UNIX 命令不同。可以看出, -kr -i8 两个选项格式化出来的代码已经很符合本章介绍的代码风格了,添加了必要的缩进和空白,较长的代码行也会自动折行。美中不足的是没有添加适当的空行,因为 indent 工具也不知道哪几行代码在逻辑上是一组的,空行还是要自己动手添,当然原有的空行肯定不会被 indent 删去的。
如果你采纳本章介绍的内核编码风格,基本上 -kr -i8 这两个参数就够用了。 indent 工具也有支持其它编码风格的选项,具体请参考 Man Page 。有时候 indent 工具的确非常有用,比如某个项目中途决定改变编码风格(这很少见),或者往某个项目中添加的几个代码文件来自另一个编码风格不同的项目,但绝不能因为有了 indent 工具就肆无忌惮
 
第十章 gdb
本章我们介绍一种很强大的调试工具 gdb ,可以完全操控程序的运行,使得程序就像你手里的玩具一样,叫它走就走,叫它停就停,并且随时可以查看程序中所有的内部状态,比如各变量的值、传给函数的参数、当前执行的代码行等。掌握了 gdb 的用法之后,调试手段就更加丰富了。但要注意,即使调试手段丰富了,调试的基本思想仍然是 分析现象 -> 假设错误原因 -> 产生新的现象去验证假设 这样一个循环,根据现象如何假设错误原因,以及如何设计新的现象去验证假设,这都需要非常严密的分析和思考,如果因为手里有了强大的工具就滥用而忽略了分析过程,往往会治标不治本地修正 Bug ,导致一个错误现象消失了但 Bug 仍然存在,甚至是把程序越改越错。本章通过初学者易犯的几个错误实例来讲解如何使用 gdb 调试程序,在每个实例后面总结一部分常用的 gdb 命令。
 
在编译时要加上 -g 选项,生成的可执行文件才能用 gdb 进行源码级调试:
 
gdb 提供一个类似 Shell 的命令行环境,上面的 (gdb) 就是提示符,在这个提示符下输入 help 可以查看命令的类别
 
也可以进一步查看某一类别中有哪些命令,例如查看 files 类别下有哪些命令可用:
 
可见 gcc -g 选项并不是把源代码嵌入到可执行文件中的,在调试时也需要源文件。现在把源代码恢复原样,我们继续调试。首先用 start 命令开始执行程序:
 
gdb 停在 main 函数中变量定义之后的第一条语句处等待我们发命令, gdb 列出的这条语句是即将执行的下一条语句。我们可以用 next 命令(简写为 n )控制这些语句一条一条地执行:
 
n 命令依次执行两行赋值语句和一行打印语句,在执行打印语句时结果立刻打出来了,然后停在 return 语句之前等待我们发命令。虽然我们完全控制了程序的执行,但仍然看不出哪里错了,因为错误不在 main 函数中而在 add_range 函数中,现在用 start 命令重新来过,这次用 step 命令(简写为 s )钻进 add_range 函数中去跟踪执行:
 
这次停在了 add_range 函数中变量定义之后的第一条语句处。在函数中有几种查看状态的办法, backtrace 命令(简写为 bt )可以查看函数调用的栈帧:
 
可见当前的 add_range 函数是被 main 函数调用的, main 传进来的参数是 low=1, high=10 main 函数的栈帧编号为 1 add_range 的栈帧编号为 0 。现在可以用 info 命令(简写为 i )查看 add_range 函数局部变量的值
 
如果想查看 main 函数当前局部变量的值也可以做到,先用 frame 命令(简写为 f )选择 1 号栈帧然后再查看局部变量