GCC警告选项例解
程序员是追求完美的一族,即使是一般的程序员大多也都不想看到自己的程序中有甚至那么一点点的瑕疵。遇到任意一条编译器警告都坚决不放过。有人会说:我们可以使用比编译器更加严格的静态代码检查工具,如
splint
。 这个建议也很不错。不过lint工具使用起来较繁琐,有时候还需要记住一些特定符号并插入到你自己的代码中才行,门槛较高,这也让很多人止步于此。那么我 们就从此放弃么?不,如今的编译器做得都很好,它可以帮助我们的找到绝大多数可能出现问题的代码,前提是你要学会控制编译器去找到这些问题代码,而熟悉编 译器的警告选项恰恰是体现控制力的好方法。当你可以自如控制编译器警告输出的时候,你就算是’入道’了,同时你对语言的理解也更进一步了。
有人说:我就是用一个-Wall选项就可以了,一般选手可以这么做,而且他可以不知道-Wall会跟踪哪些类型的问题;但是高级选手是不会只使用- Wall的,他会把每条警告都研究的很透彻,会在Makefile中列出他想让编译器输出哪些类型的警告以替代-Wall,他会屏蔽掉那些对他的代码’毫 无用处’的警告(很可能他使用了编译器对语言的扩展功能),他会有个和编译器交流的过程。
俗话说:’工欲善其事,必先利其器’,一直在工作中使用
GNU C
编译器(以下简称GCC),这里对GCC的一些警告选项细致的分析,并列举几个简单的例子[注1]供分析参考。
1. -Wall集合警告选项
我们平时可能大多数情况只使用-Wall编译警告选项,实际上-Wall选项是一系列警告编译选项的集合。下面逐一分析这一集合中的各个选项:
[-Wchar-subscripts]
如果数组使用char类型变量做为下标值的话,则发出警告。因为在某些平台上char可能默认为signed char,一旦溢出,就可能导致某些意外的结果。
e.g.
/* test_signed_char.c */
#i nclude
int main () {
char c = 255; // 我们以为char是无符号的,其范围应该是[0,255]
int i = 0;
int a[256];
for (i = 0; i type `char’
其输出结果:
-1
-41Array7476
1
从输出结果来看Solaris Array/gcc 3.2上char默认实现类型为signed char;在Windows XP/gcc-3.4.2上也是一样。
Windows上的输出结果:
-1
16 (随机值)
1
[-Wcomment]
当’/*’出现在 ’/* ... */’注释中,或者’\’出现在’// ...’注释结尾处时,使用-Wcomment会给出警告。不要小觑这些马虎代码,它很可能会影响程序的运行结果。如下面的例子:
e.g.
/*
* test_comment.c
* gcc -Wcomment test_comment.c
*/
#i nclude
int main() {
int a = 1;
int b = 2;
int c = 0; // ok just test\
c = a + b;
/*
* 这里我们期待c = 3
* /* 但实际上输出c = 0
*/
printf("the c is %d\n", c);
return 0;
}
gcc -Wcomment test_comment.c
test_comment.c:10:30: warning: multi-line comment
test_comment.c:15:12: warning: "/*" within comment
输出:
the c is 0
[-Wformat]
检查printf和scanf等格式化输入输出函数的格式字符串与参数类型的匹配情况,如果发现不匹配则发出警告。某些时候格式字符串与参数类型的不匹配会导致程序运行错误,所以这是个很有用的警告选项。
e.g.
/*
* test_format.c
*/
#i nclude
int main() {
long l = 1;
double d = 55.67;
printf("%d\n", l);
printf("%d\n", d);
return 0;
}
gcc -Wformat test_format.c
test_format.c: In function `main’:
test_format.c:10: warning: int format, long int arg (arg 2)
test_format.c:11: warning: int format, double arg (arg 2)
输出:
1
1078711746
[-Wimplicit]
该警告选项实际上是-Wimplicit-int和-Wimplicit-function-declaration两个警告选项的集合。前者在声明函数却未指明函数返回类型时给出警告,后者则是在函数声明前调用该函数时给出警告。
e.g.
/*
* test_implicit.c
*/
#i nclude
add(int a, int b) { //函数没有声明返回类型
return a + b;
}
int test() {
int a = 0;
int b = 0;
int c = 0;
int d = 0;
c = add(a, b);
d = sub(a, b); //未声明sub的函数原型
return 0;
}
gcc -Wimplicit -c test_implicit.c
test_implicit.c:7: warning: return type defaults to `int’
test_implicit.c: In function `test’:
test_implicit.c:18: warning: implicit declaration of function `sub’
[-Wmissing-braces]
当聚合类型或者数组变量的初始化表达式没有’充分’用括号{}括起时,给出警告。文字表述很难理解,举例说明则清晰些。看下面的例子:
e.g.
/*
* test_missing_braces.c
*/
struct point {
int x;
int y;
};
struct line {
struct point start;
struct point end;
};
typedef struct line line;
int main() {
int array1[2][2] = {11, 12, 13, 14};
int array2[2][2] = {{11, 12}, {13, 14}}; // ok
line l1 = {1, 1, 2, 2};
line l2 = {{2, 2}, {3, 3}}; // ok
return 0;
}
gcc -Wmissing-braces test_missing_braces.c
test_missing_braces.c: In function `main’:
test_missing_braces.c:1Array: warning: missing braces around initializer
test_missing_braces.c:1Array: warning: (near initialization for `array1[0]’)
test_missing_braces.c:21: warning: missing braces around initializer
test_missing_braces.c:21: warning: (near initialization for `l1.start’)
[-Wparentheses]
这是一个很有用的警告选项,它能帮助你从那些看起来语法正确但却由于操作符优先级或者代码结构’障眼’而导致错误运行的代码中解脱出来。好长的一个长句,还是看例子理解吧!:)
e.g.
/*
* test_parentheses.c
* gcc -Wparentheses test_parentheses.c
*/
#i nclude
int main() {
int a = 1;
int b = 1;
int c = 1;
int d = 1;
if (a && b || c) { // 人们很难记住逻辑操作符的操作顺序,所以编译器建议加上()
;
}
if (a == 12)
if (b)
d = Array;
else
d = 10; //从代码的缩进上来看,这句仿佛是if (a == 12)的else分支
printf("the d is %d\n", d); //期待d = 10, 而结果却是1
return 0;
}
gcc -Wparentheses test_parentheses.c
test_parentheses.c: In function `main’:
test_parentheses.c:13: warning: suggest parentheses around && within ||
test_parentheses.c:17: warning: suggest explicit braces to avoid ambiguous `else’
输出:
the d is 1
[-Wsequence-point]
关于顺序点(sequence point),在C标准中有解释,不过很晦涩。我们在平时编码中尽量避免写出与实现相关、受实现影响的代码便是了。而-Wsequence-point选项恰恰可以帮我们这个忙,它可以帮我们查出这样的代码来,并给出其警告。
e.g.
/*
* test_sequence_point.c
* gcc -Wsequence-point test_sequence_point.c
*/
#i nclude
int main() {
int i = 12;
i = i--;
printf("the i is %d\n", i);
return 0;
}
gcc -Wsequence-point test_sequence_point.c
test_sequence_point.c: In function `main’:
test_sequence_point.c:10: warning: operation on `i’ may be undefined
在两个平台上给出的编译警告都是一致的,但是输出结果却大相径庭。
Solaris输出:
the i is 11
Windows输出:
the i is 12
类似的像这种与顺序点相关的代码例子有:
i = i++;
a = b[i++]
a[i++] = i
等等...
[-Wswitch]
这个选项的功能浅显易懂,通过文字描述也可以清晰的说明。当以一个枚举类型(enum)作为switch语句的索引时但却没有处理default情况,或者没有处理所有枚举类型定义范围内的情况时,该选项会给处警告。
e.g.
/*
* test_switch1.c
*/
enum week {
SUNDAY,
MONDAY,
TUESDAY /* only an example , we omitted the others */
};
int test1() {
enum week w = SUNDAY;
switch(w) {
case SUNDAY:
break; // without default or the other case handlings
};
return 0;
}
int test2() { // Ok, won’t invoke even a warning
enum week w = SUNDAY;
switch(w) {
case SUNDAY:
break;
default:
break;
};
return 0;
}
int test3() { // Ok, won’t invoke even a warning
enum week w = SUNDAY;
switch(w) {
case SUNDAY:
break;
case MONDAY:
break;
case TUESDAY:
break;
};
return 0;
}
gcc -Wswitch -c test_switch.c
test_switch.c: In function `test1’:
test_switch.c:16: warning: enumeration value `MONDAY’ not handled in switch
test_switch.c:16: warning: enumeration value `TUESDAY’ not handled in switch
[-Wunused]
-Wunused是-Wunused-function、-Wunused-label、-Wunused-variable、-Wunused-value选项的集合,-Wunused-parameter需单独使用。
(1) -Wunused-function用来警告存在一个未使用的static函数的定义或者存在一个只声明却未定义的static函数,参见下面例子中的func1和func2;
(2) -Wunused-label用来警告存在一个使用了却未定义或者存在一个定义了却未使用的label,参加下面例子中的func3和func7;
(3) -Wunused-variable用来警告存在一个定义了却未使用的局部变量或者非常量static变量;参见下面例子中func5和var1;
(4) -Wunused-value用来警告一个显式计算表达式的结果未被使用;参见下面例子中func6
(5) -Wunused-parameter用来警告一个函数的参数在函数的实现中并未被用到,参见下面例子中func4。
下面是一个综合的例子
e.g.
/*
* test_unused.c
*/
static void func1(); //to prove function used but never defined
static void func2(); //to prove function defined but not used
static void func3(); //to prove label used but never defined
static void func7(); //to prove label defined but never used
static void func4(int a); //to prove parameter declared but not used
static void func5(); //to prove local variable defined but not used
static void func6(); //to prove value evaluated but not used
static int var1;
void test() {
func1();
func3();
func4(4);
func5();
func6();
}
static void func2() {
; // do nothing
}
static void func3() {
goto over;
}
static void func4(int a) {
; // do nothing
}
static void func5() {
int a = 0;
}
static void func6() {
int a = 0;
int b = 6;
a + b;
}
gcc -Wunused-parameter -c test_unused.c //如果不是用-Wunused-parameter,则func4函数将不被警告。
test_unused.c: In function `func3’:
test_unused.c:30: label `over’ used but not defined
test_unused.c: In function `func7’:
test_unused.c:35: warning: deprecated use of label at end of compound statement
test_unused.c:34: warning: label `over’ defined but not used
test_unused.c: In function `func4’:
test_unused.c:37: warning: unused parameter `a’
test_unused.c: In function `func5’:
test_unused.c:42: warning: unused variable `a’
test_unused.c: In function `func6’:
test_unused.c:48: warning: statement with no effect
test_unused.c: At top level:
test_unused.c:6: warning: `func1’ used but never defined
test_unused.c:25: warning: `func2’ defined but not used
test_unused.c:14: warning: `var1’ defined but not used
[-Wuninitialized]
该警告选项用于检查一个局部自动变量在使用之前是否已经初始化了或者在一个longjmp调用可能修改 一个non-volatile automatic variable时给出警告。目前编译器还不是那么smart,所以对有些可以正确按照程序员的意思运行的代码还是给出警告。而且该警告选项需要和’- O’选项一起使用,否则你得不到任何uinitialized的警告。
e.g.
/*
* test_uninitialized.c
*/
int test(int y) {
int x;
switch (y) {
case 1:
x = 11;
break;
case 2:
x = 22;
break;
case 3:
x = 33;
break;
}
return x;
}
gcc -Wuninitialized -O -c test_uninitialized.c
test_uninitialized.c: In function `test’:
test_uninitialized.c:6: warning: `x’ might be used uninitialized in this function
2、非-Wall集合警告选项
以下讨论的这些警告选项并不包含在-Wall中,需要程序员显式添加。
[-Wfloat-equal]
该项用来检查浮点值是否出现在相等比较的表达式中。
e.g.
/*
* test_float_equal.c
*/
void test(int i) {
double d = 1.5;
if (d == i) {
;
}
}
gcc -Wfloat-equal -c test_float_equal.c
test_float_equal.c: In function `test’:
test_float_equal.c:8: warning: comparing floating point with == or != is unsafe
[-Wshadow]
当局部变量遮蔽(shadow)了参数、全局变量或者是其他局部变量时,该警告选项会给我们以警告信息。
e.g.
/*
* test_shadow.c
*/
int g;
void test(int i) {
short i;
double g;
}
gcc -Wshadow -c test_shadow.c
test_shadow.c: In function `test’:
test_shadow.c:Array: warning: declaration of `i’ shadows a parameter
test_shadow.c:10: warning: declaration of `g’ shadows a global declaration
test_shadow.c:6: warning: shadowed declaration is here
[-Wbad-function-cast]
当函数(准确地说应该是函数返回类型)被转换为非匹配类型时,均产生警告。
e.g.
/*
* test_bad_func_case.c
*/
int add(int a, int b) {
return a+b;
}
void test() {
char *p = (char*)add(1, 13);
}
gcc -Wbad-function-cast -c test_bad_func_case.c
test_bad_func_case.c: In function `test’:
test_bad_func_case.c:11: warning: cast does not match function type
[-Wcast-qual]
当去掉修饰源Target的限定词(如const)时,给出警告。
e.g.
/*
* test_cast_qual.c
*/
void test() {
char c = 0;
const char *p = &c;
char *q;
q = (char*)p;
}
gcc -Wcast-qual -c test_cast_qual.c
test_cast_qual.c: In function `test’:
test_cast_qual.c:10: warning: cast discards qualifiers from pointer target type
[-Wcast-align]
这是个非常有用的选项,特别是对于在Solaris这样的对内存对齐校验的平台尤其重要。它用于在从对齐系数小的地址(如char*)转换为对齐系数大的地址(如int*)转换时给出警告。
e.g.
/*
* test_cast_align.c
*/
#i nclude
int main() {
char c = 1;
char *p = &c; //ok
int *q = (int*)p; //bad align-cast
printf("the *q is %d\n", *q);
return 0;
}
gcc -Wcast-align test_cast_align.c
test_cast_align.c: In function `main’:
test_cast_align.c:Array: warning: cast increases required alignment of target type
输出:
总线错误 ((主存储器)信息转储) //on Solaris Array
[-Wsign-compare]
在有符号数和无符号数进行值比较时,有符号数可能在比较之前被转换为无符号数而导致结果错误。使用该选项会对这样的情况给出警告。
e.g.
/*
* test_sign_compare.c
*/
#i nclude
int main() {
unsigned int i = 128;
signed int j = -1;
if (i j\n");
}
return 0;
}
gcc -Wsign-compare test_sign_compare.c
test_sign_compare.c: In function `main’:
test_sign_compare.c:10: warning: comparison between signed and unsigned
输出:
i type
test_unreachable.c:Array: warning: will never be executed
[-Wconvertion]
由于原型定义而引起的定点和浮点数之间的隐式转换(强制转换)或者由有符号数和无符号数之间隐式转换转换引起的警告。
e.g.
/*
* test_conversion.c
*/
#i nclude
void getdouble(double d) {
; // do nothing
}
int main() {
unsigned int k;
int n = 12;
k = -1;
k = (unsigned int)-1; // ok, explicit conversion ,no warning
getdouble(n);
return 0;
}
gcc -Wconversion test_conversion.c
test_conversion.c: In function `main’:
test_conversion.c:15: warning: negative integer implicitly converted to unsigned type
test_conversion.c:18: warning: passing arg 1 of `getdouble’ as floating rather than integer due to prototype
3、-Wtraditional和-W
这两个警告选项其实也都是一些组合(大部分都在上面提到过),前者用来在代码中使用了标准C不同于传统C的特性时,发出警告;后者也是针对一些事件打开一个警告集合。关于它们的说明具体可参见’
Using the GNU Compiler Collection
’。
GCC警告选项例解的更多相关文章
- 【转】GCC警告选项例解 -- 不错
原文网址:http://blog.csdn.net/hcx25909/article/details/7383716 程序员是追求完美的一族,即使是一般的程序员大多也都不想看到自己的程序中有甚至那么一 ...
- gcc及其选项详解 【转载】
1.简介: gcc是gnu旗舰产品,目前基本上就是和unix捆绑在一起分发的.这个东西功能强大,但是有多达上千个选项,其用户手册也有将近一万行.虽然其中的多数选项平时很少用到.但是不管装软件还是写程序 ...
- gcc及其选项详解
1.简介: gcc是gnu旗舰产品,目前基本上就是和unix捆绑在一起分发的.这个东西功能强大,但是有多达上千个选项,其用户手册也有将近一万行.虽然其中的多数选项平时很少用到.但是不管装软件还是写程序 ...
- GCC 常用选项详解
参考gcc man page 参考:http://www.cppblog.com/seman/archive/2005/11/30/1440.html gcc and g++分别是gnu的c & ...
- GCC 警告提示的用法
转自GCC 警告提示的用法 本节主要讲解GCC的警告提示功能.GCC包含完整的出错检查和警告提示功能,它们可以帮助Linux程序员写出更加专业和优美的代码.我们千万不能小瞧这些警告信息,在很多情况下, ...
- GCC(警告.优化以及调试选项)
GCC(警告.优化以及调试选项) [介绍] gcc and g++分别是gnu的c & c++编译器 gcc/g++在执行编译工作的时候,总共需要4步 1.预处理,生成.i的文件 预处 ...
- GCC编译警告选项总结
一 前言 GCC有很多的编译选项,警告选项:指定头文件.库路径:优化选项.本文针整理一下GCC的警告选项,主要依据http://gcc.gnu.org/onlinedocs/gcc/Warning-O ...
- Linux 之 编译器 gcc/g++参数详解
2016年12月9日16:48:53 ----------------------------- 内容目录: [介绍] gcc and g++分别是gnu的c & c++编译器 gcc/g++ ...
- scons用户指南翻译(附gcc/g++参数详解)
scons用户指南 翻译 http://blog.csdn.net/andyelvis/article/category/948141 官网文档 http://www.scons.org/docume ...
随机推荐
- linux find 反转 查找没有被找到的结果
在linux下,有时候需要找一些文件,还有时候这些文件格式不够统一和规范,但是需要排除的那些文件却格式统一,就可以使用find命令的反转功能 一般用find查找文件的命令是: find . -name ...
- Spark机器学习 Day2 快速理解机器学习
Spark机器学习 Day2 快速理解机器学习 有两个问题: 机器学习到底是什么. 大数据机器学习到底是什么. 机器学习到底是什么 人正常思维的过程是根据历史经验得出一定的规律,然后在当前情况下根据这 ...
- 概念:RPG与RPGLE的区别
RPG是OPM编程模式,即RPG编程的代码不能编译成*MODULE:编译只能直接生成一个程序,*PGM. RPGLE是ILE编程模式.OS/400环境下,ILE是集成开发环境.在ILE环境下,所 ...
- Wpf 简单制作自己的窗体样式(2)
上一篇blog讲了制作简单的样式的窗体,对于一个传统的窗体,不仅仅可以拖动,和关闭操作.还具有最大化.最小化.隐藏,以及改变窗体的大小等.这篇blog就是对上篇的补充,完善窗体的改变大小和最大化最小化 ...
- Speech Patterns (string)
People often have a preference among synonyms of the same word. For example, some may prefer "t ...
- 外部表查询时出现ORA-29913和ORA-29400错误
create table t_ext_tab(id char(1),name char(6)) organization external( type oracle_loader default di ...
- 【Nhibernate】HQL 分页
HQL IQuery query = NHibernateHelper.OpenSession() .CreateQuery( @"from Product"); query.Se ...
- Oracle的spool命令
在控制台上使用spool 路径+文件名 命令可以将整个过程写入指定的文件中, 结束使用spool off 命令, 当执行spool off后文件中的内容才能看见; ed命令修改当前缓冲区的上一条命令;
- iOS9下修改回HTTP模式进行网络请求
升级为iOS9后,默认请求类型为https,如何使用http进行请求会报错 The resource could not be loaded because the App Transport Sec ...
- wget 下载百度云jdk
oracle官网下载需要登录下载 所以从百度云下载 wget -c -O "URL"