程序员是追求完美的一族,即便是通常的程序员大多也都不想看到本身的程序中有甚至那么一点点的瑕疵。遇到任意一条编译器警告都坚定不放过。有人会说:咱们可使用比编译器更加严格的静态代码检查工具,如splint。 这个建议也很不错。不过lint工具使用起来较繁琐,有时候还须要记住一些特定符号并插入到你本身的代码中才行,门槛较高,这也让不少人止步于此。那么我 们就今后放弃么?不,现在的编译器作得都很好,它能够帮助咱们的找到绝大多数可能出现问题的代码,前提是你要学会控制编译器去找到这些问题代码,而熟悉编 译器的警告选项偏偏是体现控制力的好方法。当你能够自如控制编译器警告输出的时候,你就算是'入道'了,同时你对语言的理解也更进一步了。c++
有人说:我就是用一个-Wall选项就能够了,通常选手能够这么作,并且他能够不知道-Wall会跟踪哪些类型的问题;可是高级选手是不会只使用- Wall的,他会把每条警告都研究的很透彻,会在Makefile中列出他想让编译器输出哪些类型的警告以替代-Wall,他会屏蔽掉那些对他的代码'毫 无用处'的警告(极可能他使用了编译器对语言的扩展功能),他会有个和编译器交流的过程。程序员
俗话说:'工欲善其事,必先利其器',一直在工做中使用GNU C编译器(如下简称GCC),这里对GCC的一些警告选项细致的分析,并列举几个简单的例子[注1]供分析参考。数组
[-Wall]less
咱们平时可能大多数状况只使用-Wall编译警告选项,实际上-Wall选项是一系列警告编译选项的集合,一般可使用-Wall来开启如下警告:
-Waddress -Warray-bounds (only with -O2) -Wc++0x-compat
-Wchar-subscripts -Wimplicit-int -Wimplicit-function-declaration
-Wcomment -Wformat -Wmain (only for C/ObjC and unless
-ffreestanding) -Wmissing-braces -Wnonnull -Wparentheses
-Wpointer-sign -Wreorder -Wreturn-type -Wsequence-point
-Wsign-compare (only in C++) -Wstrict-aliasing -Wstrict-overflow=1
-Wswitch -Wtrigraphs -Wuninitialized (only with -O1 and above)
-Wunknown-pragmas -Wunused-function -Wunused-label -Wunused-value
-Wunused-variable
unused-function:警告声明可是没有定义的static函数;
unused- label:声明可是未使用的标签;
unused-parameter:警告未使用的函数参数;
unused-variable:声明但 是未使用的本地变量;
unused-value:计算了可是未使用的值;
format:printf和scanf这样的函数中的格式字符 串的使用不当;
implicit-int:未指定类型;
implicit-function:函数在声明前使用;
char- subscripts:使用char类做为数组下标(由于char多是有符号数);
missingbraces:大括号不匹配;
parentheses: 圆括号不匹配;
return-type:函数有无返回值以及返回值类型不匹配;
sequence-point:违反顺序点的代码,好比 a[i] = c[i++];
switch:switch语句缺乏default或者switch使用枚举变量为索引时缺乏某个变量的case;
strict- aliasing=n:使用n设置对指针变量指向的对象类型产生警告的限制程度,默认n=3;只有在-fstrict-aliasing设置的状况下有 效;
unknow-pragmas:使用未知的#pragma指令;
uninitialized:使用的变量为初始化,只在-O2时有 效;
如下是在-Wall中不会激活的警告选项:
cast-align:当指针进行类型转换后有内存对齐要求更严格时发出警告;
sign- compare:当使用signed和unsigned类型比较时;
missing-prototypes:当函数在使用前没有函数原型时;
packed:packed 是gcc的一个扩展,是使结构体各成员之间不留内存对齐所需的空 间,有时候会形成内存对齐的问题;
padded:也是gcc的扩展,使结构体成员之间进行内存对齐的填充,会 形成结构体体积增大.
unreachable-code:有不会执行的代码时.
inline:当inline函数再也不保持inline时 (好比对inline函数取地址);
disable-optimization:当不能执行指定的优化时.(须要太多时间或系统资源).函数
在编译一些项目的时候能够-W和-Wall选项一块儿使用。可使用 -Werror时全部的警告都变成错误,使出现警告时也中止编译.须要和指定警告的参数一块儿使用.工具
-w 关闭编译时的警告,也就是编译后不显示任何warning,由于有时在编译以后编译器会显示一些例如数据转换之类的警告,这些警告是咱们平时能够忽略的。优化
-W 选项相似-Wall,会显示警告,可是只显示编译器认为会出现错误的警告。ui
[-Wchar-subscripts]
若是数组使用char类型变量作为下标值的话,则发出警告。由于在某些平台上char可能默认为signed char,一旦溢出,就可能致使某些意外的结果。this
/* test_signed_char.c */ #include<stdio.h> int main ()
{ char c = 255; // 咱们觉得char是无符号的,其范围应该是[0,255] int i = 0; int a[256]; for (i = 0; i < 256; i++)
{ a[i] = 1; } printf("%d\n", c); // 咱们期待输出255 printf("%d\n", a[c]); // 咱们期待输出1 printf("%d\n", a[255]); return 0; }
gcc -Wchar-subscripts test_signed_char.c
test_signed_char.c: In function `main':
test_signed_char.c:13: warning: array subscript has type `char'编码
其输出结果:
-1
-4197476
1
从输出结果来看Solaris 9/gcc 3.2上char默认实现类型为signed char;在Windows XP/gcc-3.4.2上也是同样。
Windows上的输出结果:
-1
16 (随机值)
1
[-Wcomment]
当'/*'出如今 '/* ... */'注释中,或者'\'出如今'// ...'注释结尾处时,使用-Wcomment会给出警告。不要小觑这些马虎代码,它极可能会影响程序的运行结果。以下面的例子:
/* * test_comment.c * gcc -Wcomment test_comment.c */ #include<stdio.h> 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等格式化输入输出函数的格式字符串与参数类型的匹配状况,若是发现不匹配则发出警告。某些时候格式字符串与参数类型的不匹配会致使程序运行错误,因此这是个颇有用的警告选项。
/* * test_format.c */ #include<stdio.h> 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两个警告选项的集合。前者在声明函数却未指明函数返回类型时给出警告,后者则是在函数声明前调用该函数时给出警告。
/* * test_implicit.c */ #include<stdio.h> 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]
当聚合类型或者数组变量的初始化表达式没有'充分'用括号{}括起时,给出警告。文字表述很难理解,举例说明则清晰些。看下面的例子:
/* * 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:19: warning: missing braces around initializer
test_missing_braces.c:19: 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]
这是一个颇有用的警告选项,它能帮助你从那些看起来语法正确但却因为操做符优先级或者代码结构'障眼'而致使错误运行的代码中解脱出来。好长的一个长句,仍是看例子理解吧!:)
/* * test_parentheses.c * gcc -Wparentheses test_parentheses.c */ #include<stdio.h> int main() { int a = 1; int b = 1; int c = 1; int d = 1; if (a && b || c) { // 人们很难记住逻辑操做符的操做顺序,因此编译器建议加上() ; } if (a == 12) if (b) d = 9; 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选项偏偏能够帮咱们这个忙,它能够帮咱们查出这样的代码来,并给出其警告。
/* * test_sequence_point.c * gcc -Wsequence-point test_sequence_point.c */ #include<stdio.h> 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[i] = b[i++]
a[i++] = i
等等...
[-Wswitch]
这个选项的功能浅显易懂,经过文字描述也能够清晰的说明。当以一个枚举类型(enum)做为switch语句的索引时但却没有处理default状况,或者没有处理全部枚举类型定义范围内的状况时,该选项会给处警告。
/* * 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。
下面是一个综合的例子
/* * 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的警告。
/* * 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
二、非-Wall集合警告选项
如下讨论的这些警告选项并不包含在-Wall中,须要程序员显式添加。
[-Wfloat-equal]
该项用来检查浮点值是否出如今相等比较的表达式中。
/* * 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)了参数、全局变量或者是其余局部变量时,该警告选项会给咱们以警告信息。
/* * 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:9: 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]
当函数(准确地说应该是函数返回类型)被转换为非匹配类型时,均产生警告。
/* * 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)时,给出警告。
/* * 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 targettype
[-Wcast-align]
这是个很是有用的选项,特别是对于在Solaris这样的对内存对齐校验的平台尤为重要。它用于在从对齐系数小的地址(如char*)转换为对齐系数大的地址(如int*)转换时给出警告。
/* * test_cast_align.c */ #include <stdio.h> 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:9: warning: cast increases required alignment of target type
输出:
总线错误 ((主存储器)信息转储) //on Solaris 9
[-Wsign-compare]
在有符号数和无符号数进行值比较时,有符号数可能在比较以前被转换为无符号数而致使结果错误。使用该选项会对这样的状况给出警告。
/* * test_sign_compare.c */ #include<stdio.h> int main() { unsigned int i = 128; signed int j = -1; if (i < j) { printf("i < j\n"); } else { printf("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 < j
[-Waggregate-return]
若是一个函数返回一个聚合类型,如结构体、联合或者数组,该选项就会给出警告信息。较简单不举例了。
[-Wmultichar]
当咱们写下如此代码时:char c = 'peter', 使用该选项会给出警告。这个选项是默认选项,你无需单独使用该选项,不过你可使用-Wno-multichar来关闭这些警告信息,可是这但是不建议你 去作的。对于char c = 'peter'这样的代码的处理是与平台相关,不可移植的。
/* * test_multichar.c */ int main() { char c = 'peter'; printf("c is %c\n", c); return 0; }
但这里在Windows和Solaris平台输出的结果却一致:
c is r
[-Wunreachable-code]
这个选项是一个检查冗余代码或疏忽代码好办法。它一旦检查到你的代码中有不可达的代码,就会发出警告。这些代码每每会存在潜在的危机。
/* * test_unreachable.c */ int test(char c) { if (c < 256) { return 0; } else { return 1; } }
gcc -Wunreachable-code -c test_unreachable.c
test_unreachable.c: In function `test':
test_unreachable.c:6: warning: comparison is always true due to limited range of data type
test_unreachable.c:9: warning: will never be executed
[-Wconvertion]
因为原型定义而引发的定点和浮点数之间的隐式转换(强制转换)或者由有符号数和无符号数之间隐式转换转换引发的警告。
/* * test_conversion.c */ #include<stdio.h> 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 unsignedtype
test_conversion.c:18: warning: passing arg 1 of `getdouble' as floating rather than integer due to prototype
三、-Wtraditional和-W这两个警告选项其实也都是一些组合(大部分都在上面提到过),前者用来在代码中使用了标准C不一样于传统C的特性时,发出警告;后者也是针对一些事件打开一个警告集合。关于它们的说明具体可参见'Using the GNU Compiler Collection'。