GCC 編譯選項


程序員是追求完美的一族,即使是一般的程序員大多也都不想看到自己的程序中有甚至那么一點點的瑕疵。遇到任意一條編譯器警告都堅決不放過。有人會說:我們可以使用比編譯器更加嚴格的靜態代碼檢查工具,如splint。 這個建議也很不錯。不過lint工具使用起來較繁瑣,有時候還需要記住一些特定符號並插入到你自己的代碼中才行,門檻較高,這也讓很多人止步於此。那么我 們就從此放棄么?不,如今的編譯器做得都很好,它可以幫助我們的找到絕大多數可能出現問題的代碼,前提是你要學會控制編譯器去找到這些問題代碼,而熟悉編 譯器的警告選項恰恰是體現控制力的好方法。當你可以自如控制編譯器警告輸出的時候,你就算是'入道'了,同時你對語言的理解也更進一步了。

有人說:我就是用一個-Wall選項就可以了,一般選手可以這么做,而且他可以不知道-Wall會跟蹤哪些類型的問題;但是高級選手是不會只使用- Wall的,他會把每條警告都研究的很透徹,會在Makefile中列出他想讓編譯器輸出哪些類型的警告以替代-Wall,他會屏蔽掉那些對他的代碼'毫 無用處'的警告(很可能他使用了編譯器對語言的擴展功能),他會有個和編譯器交流的過程。

俗話說:'工欲善其事,必先利其器',一直在工作中使用GNU C編譯器(以下簡稱GCC),這里對GCC的一些警告選項細致的分析,並列舉幾個簡單的例子[注1]供分析參考。

[-Wall]

我們平時可能大多數情況只使用-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,會顯示警告,但是只顯示編譯器認為會出現錯誤的警告。

 

[-Wchar-subscripts]
如果數組使用char類型變量做為下標值的話,則發出警告。因為在某些平台上char可能默認為signed char,一旦溢出,就可能導致某些意外的結果。

/* 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

 

2、非-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

 

3、-Wtraditional和-W
這兩個警告選項其實也都是一些組合(大部分都在上面提到過),前者用來在代碼中使用了標准C不同於傳統C的特性時,發出警告;后者也是針對一些事件打開一個警告集合。關於它們的說明具體可參見'Using the GNU Compiler Collection'。


免責聲明!

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



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