GCC 編譯選項

程序員是追求完美的一族,即便是通常的程序員大多也都不想看到本身的程序中有甚至那麼一點點的瑕疵。遇到任意一條編譯器警告都堅定不放過。有人會說:咱們可使用比編譯器更加嚴格的靜態代碼檢查工具,如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'。

相關文章
相關標籤/搜索