編譯器的warning往往不只是警告


    編譯警告warning是所有的程序員都不可能不會遇到的一件事情,而且往往在一個很大的project里面這更是司空見慣的事情,如果那一天你敲下Make的瞬間少了那些一串串的“warning:”,你可能反而會懷疑是不是哪兒出來問題/_\  雖然說每一個人的編程習慣,個人素養不一樣,對待warning的態度也不盡一致,但是總得來說80%的程序員都是抱着“warning is okay”的態度;追根溯源,為什么編譯器會有Error和warning之分呢?通俗的來說,編譯器確定不允許的就認為是Error,然后一些違背原則但是編譯器又不確定的就定義為warning,既然是不確定,那就有一個限度的概念,所以編譯器都允許定制warning的級別,但是建議在Makfile中打開“Wall"的選項,這樣讓更多的問題暴露無遺。所以說warning是編譯器為程序員提供的友善建議和意見,即便編譯器這么熱心和藹,但是仍然有很多程序員對Warning不以為然。下面就簡單總結幾個GNU GCC為我們編譯時候發生warning的情況下,如果你不以為然,那么你將在稍后運行的時候變得很迷茫,苦惱和尷尬……

    CASE 1:

/* this is main.c */
int main(int argc, char **argv)
{
	foo();

	return 0;
}

/*This is foo.c*/
int foo(char *s, int len)
{
	*(s + len) = 0;

	return 0;
}
Kevins-MacBook-Pro:Test kinreven$ gcc -c -Wall main.c -o main.o && gcc -c -Wall foo.c -o foo.o
main.c: In function ‘main’:
main.c:3: warning: implicit declaration of function ‘foo’
Kevins-MacBook-Pro:Test kinreven$ gcc main.o foo.o -o main.exe

    上面的implicit declaration 估計是最常見過的warning之一,這種問題多出在模塊於模塊之間缺少header file的溝通,或者直接調用其他模塊內部的function造成。有時候是因為漏include了該模塊的header file,有時候是由於架構設計的問題導致需要的interface沒有暴露出來,但是更更多情況是由於debug或者一次hack需要直接call到其他模塊內部。這樣即便是你的調用形式和實現的函數原型沖突都不會引發編譯錯誤。因為在compile時候,都是依據header file里面申明的函數原型和調用進行check,如果沒有函數的申明,那么compiler僅僅是拋出”implicit declaration“的warning,而在Link的時候,只要其他lib里面能夠找到這樣的函數名,那么根據符號匹配就能Link成功。但是當你在運行時候,如上面的case就要引發諸如”段錯誤“等問題導致crash。所以正確的做法應該是include其他模塊的header file,這樣如果調用的時候參數類型和個數不匹配便會發生Compile Error。

    CASE 2:

/* this is main.c */
char a,b;
void f(int *a, int *b)
{
	int t;
	t = *a;
	*a = *b;
	*b = t;
}
void g(void)
{
	f(&a, &b);
}
Kevins-MacBook-Pro:Test kinreven$ gcc -Wall main.c -o main.exe
main.c: In function ‘g’:
main.c:12: warning: passing argument 1 of ‘f’ from incompatible pointer type
main.c:12: warning: passing argument 2 of ‘f’ from incompatible pointer type

    上面的incompatible type又是一常見warning,這樣的問題在大多數情況下面應該是okay的,因為C會進行隱式類型轉換,但是像上面的case估計就踩到雷區了,可能他的輸出就未必是你想要的了:> 因為a,b是char類型占1byte,而f()的兩個pointer都是int *,所以在里面進行調換的時候就會發生覆蓋的情況,要想知道結果就自己試試吧

    CASE 3:

/* this is main.c */
void f(void)
{
	char c;
	for (c=0; c<=255; ++c) {
	/* ... */
	}
}
Kevins-MacBook-Pro:Test kinreven$ gcc -Wall main.c -o main.exe
main.c: In function ‘f’:
main.c:5: warning: comparison is always true due to limited range of data type

    上面的warning寫的很清楚,但是你如果不看估計也未必能發現你是多么的傻,估計在C的第一章節就會講到常用的數據類型,然后老師還會強調每一種數據類型的長度,char在大多數的系統里面都應該是1byte,所以他的取值區間是-128 ~ 127,所以這里的++c 無論如何都是<255 ,所以這里永遠都是ture,這樣就產生了你不預期的死循環。

    CASE 4:

/* this is main.c */
int *a, *b;
void f(int delta, int base)
{
	unsigned int u = a - b;
	if (u - delta < base) {
		; /*...*/
	}
}

   上面function在比較舊的GCC版本上面是會產生一條”comparison between signed and unsigned integer expressions“的警告,但是我在罪行GCC 4.x上面在加"-Wall -Wconversion -ansi"選項后均為產生warning信息。但是上面的隱式轉換往往會給你帶來非預期的結果,比如上面的delta盒unsigned ,然而delta是int,所以在表達式u - delta中,delta會隱式的轉換成unsigned,如果delta是一個負數就會轉換為一個很大的整數,這樣的結果可能並不是你期望的/_\

 所以綜上,我們應當認真對待warning,如果你不以為然,有時候他會讓你后悔;養成好習慣,清除warning,讓built windows更干凈,讓compiler更happy。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM