文/一觉亮天
由于C语言的灵活和设计上的追求高效率,在写C程序的时候,经常会语法正确,但隐含逻辑错误。比如变量未初始化,switch语句没有default分支等。一些编译要求严格的语言,这种情况在编译阶段会报错或报告警,但C语言编译器却视而不见。
PC-Lint就是可以检查类似错误的工具,可以认为PC-Lint是更严格的C编译器。实际上如果我们懂得运用C语言编译器的编译选项,也可以一定程度上发现隐含的逻辑错误。下面我们以gcc编译器为例,在Redhat Enterprise Linux5.3上测试看如何让gcc编译器发现隐含未初始化变量。
如下面的程序:
通常我们编译就用gcc-c gcc_flags.c,这样的话,编译器什么也不说。
如果我们加上检查未初始化的变量的选项再试gcc-c -Wuninitialized gcc_flags.c,则gcc会提示-Wuninitialized is notsupported without -O。这告诉我们需要加-O选项,- Wuninitialized选项才会生效。-W是设置Warning的选项。-O是设置Optimization(优化)的选项,有-O,-O1,-O2等优化级别,我们用O2。
我们加上优化选项再试gcc-c -Wuninitialized -O2 gcc_flags.c,则gcc会有下面的提示:
gcc_flags.c: In function ‘tst_uninitialized1’:
gcc_flags.c:13: warning: ‘ui’ may be used uninitialized inthis f?
现在gcc可以发现未被初始化的变量了,而且gcc足够聪明,它会检查语句来推测未被初始化的变量,而不是武断地下结论。比如在函数tst_uninitialized1和tst_uninitialized2中,最初我们都声明了一个未赋初值的变量ui,但gcc只会认为tst_uninitialized1中的ui可能未被初始化,而不会认为函数tst_uninitialized2有问题,因为uninitialized2中无论函数执行了if语句的哪个分支,ui都会被初始化。
gcc编译器中这样的-W选项很多,比如-Wswitch-default选项会对没有default分支的switch语句告警,比如-Wenum-compare选项会对不同enum类型的变量比较告警,等。为了简单起见,我们可以把这些选项全都加上,用-Wall和-Wextra会包含大部分gcc支持的-W选项。更猛一点,我们可以加上-Werror,这样gcc会把warning当做error看待,确保我们一定去掉这些Warning才会编译成功。最后的编译选项如下:
gcc -c -Wall -Wextra -Werror -O2 gcc_flags.c