http://blog.csdn.net/hcx25909/article/details/7383716程序員
程序員是追求完美的一族,即便是通常的程序員大多也都不想看到本身的程序中有甚至那麼一點點的瑕疵。遇到任意一條編譯器警告都堅定不放過。有人會說:咱們可使用比編譯器更加嚴格的靜態代碼檢查工具,如splint。 這個建議也很不錯。不過lint工具使用起來較繁瑣,有時候還須要記住一些特定符號並插入到你本身的代碼中才行,門檻較高,這也讓不少人止步於此。那麼我 們就今後放棄麼?不,現在的編譯器作得都很好,它能夠幫助咱們的找到絕大多數可能出現問題的代碼,前提是你要學會控制編譯器去找到這些問題代碼,而熟悉編 譯器的警告選項偏偏是體現控制力的好方法。當你能夠自如控制編譯器警告輸出的時候,你就算是'入道'了,同時你對語言的理解也更進一步了。函數
有人說:我就是用一個-Wall選項就能夠了,通常選手能夠這麼作,並且他能夠不知道-Wall會跟蹤哪些類型的問題;可是高級選手是不會只使用- Wall的,他會把每條警告都研究的很透徹,會在Makefile中列出他想讓編譯器輸出哪些類型的警告以替代-Wall,他會屏蔽掉那些對他的代碼'毫 無用處'的警告(極可能他使用了編譯器對語言的擴展功能),他會有個和編譯器交流的過程。工具
俗話說:'工欲善其事,必先利其器',一直在工做中使用GNU C編譯器(如下簡稱GCC),這裏對GCC的一些警告選項細緻的分析,並列舉幾個簡單的例子[注1]供分析參考。ui
1. -Wall集合警告選項
咱們平時可能大多數狀況只使用-Wall編譯警告選項,實際上-Wall選項是一系列警告編譯選項的集合。下面逐一分析這一集合中的各個選項:this
[-Wchar-subscripts]
若是數組使用char類型變量作爲下標值的話,則發出警告。由於在某些平臺上char可能默認爲signed char,一旦溢出,就可能致使某些意外的結果。編碼
e.g.
/* test_signed_char.c */
#i ncludelua
int main () {
char c = 255; // 咱們覺得char是無符號的,其範圍應該是[0,255]
int i = 0;
int a[256];spa
for (i = 0; i < 256; i++) {
a[i] = 1;
}.net
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會給出警告。不要小覷這些馬虎代碼,它極可能會影響程序的運行結果。以下面的例子:
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: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]
這是一個頗有用的警告選項,它能幫助你從那些看起來語法正確但卻因爲操做符優先級或者代碼結構'障眼'而致使錯誤運行的代碼中解脫出來。好長的一個長句,仍是看例子理解吧!:)
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 = 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選項偏偏能夠幫咱們這個忙,它能夠幫咱們查出這樣的代碼來,並給出其警告。
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[i] = 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
二、非-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: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]
當函數(準確地說應該是函數返回類型)被轉換爲非匹配類型時,均產生警告。
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 targettype
[-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:9: warning: cast increases required alignment of target type
輸出:
總線錯誤 ((主存儲器)信息轉儲) //on Solaris 9
[-Wsign-compare]
在有符號數和無符號數進行值比較時,有符號數可能在比較以前被轉換爲無符號數而致使結果錯誤。使用該選項會對這樣的狀況給出警告。
e.g.
/*
* test_sign_compare.c
*/
#i nclude
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'這樣的代碼的處理是與平臺相關,不可移植的。
e.g.
/*
* test_multichar.c
*/
int main() {
char c = 'peter';
printf("c is %c\n", c);
return 0;
}
但這裏在Windows和Solaris平臺輸出的結果卻一致:
c is r
[-Wunreachable-code]
這個選項是一個檢查冗餘代碼或疏忽代碼好辦法。它一旦檢查到你的代碼中有不可達的代碼,就會發出警告。這些代碼每每會存在潛在的危機。
e.g.
/*
* 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]
因爲原型定義而引發的定點和浮點數之間的隱式轉換(強制轉換)或者由有符號數和無符號數之間隱式轉換轉換引發的警告。
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 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'。