程序员是追求完美的一族,即使是一般的程序员大多也都不想看到自己的程序中有甚至那么一点点的瑕疵。遇到任意一条编译器警告都坚决不放过。有人会说:我们可以使用比编译器更加严格的静态代码检查工具,如splint。这个建议也很不错。不过lint工具使用起来较繁琐,有时候还需要记住一些特定符号并插入到你自己的代码中才行,门槛较高,这也让很多人止步于此。那么我们就从此放弃么?不,如今的编译器做得都很好,它可以帮助我们的找到绝大多数可能出现问题的代码,前提是你要学会控制编译器去找到这些问题代码,而熟悉编译器的警告选项恰恰是体现控制力的好方法。当你可以自如控制编译器警告输出的时候,你就算是'入道'了,同时你对语言的理解也更进一步了。
有人说:我就是用一个-Wall选项就可以了,一般选手可以这么做,而且他可以不知道-Wall会跟踪哪些类型的问题;但是高级选手是不会只使用-Wall的,他会把每条警告都研究的很透彻,会在Makefile中列出他想让编译器输出哪些类型的警告以替代-Wall,他会屏蔽掉那些对他的代码'毫无用处'的警告(很可能他使用了编译器对语言的扩展功能),他会有个和编译器交流的过程。
俗话说:'工欲善其事,必先利其器',一直在工作中使用GNU C编译器(以下简称GCC),这里对GCC的一些警告选项细致的分析,并列举几个简单的例子[注1]供分析参考。
1. -Wall集合警告选项我们平时可能大多数情况只使用-Wall编译警告选项,实际上-Wall选项是一系列警告编译选项的集合。下面逐一分析这一集合中的各个选项:
[-Wchar-subscripts]如果数组使用char类型变量做为下标值的话,则发出警告。因为在某些平台上char可能默认为signed char,一旦溢出,就可能导致某些意外的结果。
e.g./* test_signed_char.c */#include
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.ctest_signed_char.c: In function `main':test_signed_char.c:13: warning: array subscript has type `char'
其输出结果:-1-41974761从输出结果来看Solaris 9/gcc 3.2上char默认实现类型为signed char;在Windows XP/gcc-3.4.2上也是一样。Windows上的输出结果:-116 (随机值)1
[-Wcomment]当'/*'出现在 '/* ... */'注释中,或者'\'出现在'// ...'注释结尾处时,使用-Wcomment会给出警告。不要小觑这些马虎代码,它很可能会影响程序的运行结果。如下面的例子:
e.g./* * test_comment.c * gcc -Wcomment test_comment.c */#include
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.ctest_comment.c:10:30: warning: multi-line commenttest_comment.c:15:12: warning: "/*" within comment
输出:the c is 0
[-Wformat]检查printf和scanf等格式化输入输出函数的格式字符串与参数类型的匹配情况,如果发现不匹配则发出警告。某些时候格式字符串与参数类型的不匹配会导致程序运行错误,所以这是个很有用的警告选项。
e.g./* * test_format.c */#include
int main() {
long l = 1;
double d = 55.67;
printf("%d\n", l);
printf("%d\n", d);
return 0;}
gcc -Wformat test_format.ctest_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)
输出:11078711746
[-Wimplicit]该警告选项实际上是-Wimplicit-int和-Wimplicit-function-declaration两个警告选项的集合。前者在声明函数却未指明函数返回类型时给出警告,后者则是在函数声明前调用该函数时给出警告。
e.g./* * test_implicit.c */#include
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.ctest_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.ctest_missing_braces.c: In function `main':test_missing_braces.c:19: warning: missing braces around initializertest_missing_braces.c:19: warning: (near initialization for `array1[0]')test_missing_braces.c:21: warning: missing braces around initializertest_missing_braces.c:21: warning: (near initialization for `l1.start')
[-Wparentheses]这是一个很有用的警告选项,它能帮助你从那些看起来语法正确但却由于操作符优先级或者代码结构'障眼'而导致错误运行的代码中解脱出来。好长的一个长句,还是看例子理解吧!:)
e.g./* * test_parentheses.c * gcc -Wparentheses test_parentheses.c */#include
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.ctest_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 */
#include
int main() { int i = 12; i = i--; printf("the i is %d\n", i); return 0;}
gcc -Wsequence-point test_sequence_point.ctest_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.ctest_switch.c: In function `test1':test_switch.c:16: warning: enumeration value `MONDAY' not handled in switchtest_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 definedstatic void func2(); //to prove function defined but not usedstatic void func3(); //to prove label used but never definedstatic void func7(); //to prove label defined but never usedstatic void func4(int a); //to prove parameter declared but not usedstatic void func5(); //to prove local variable defined but not usedstatic 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 definedtest_unused.c: In function `func7':test_unused.c:35: warning: deprecated use of label at end of compound statementtest_unused.c:34: warning: label `over' defined but not usedtest_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 effecttest_unused.c: At top level:test_unused.c:6: warning: `func1' used but never definedtest_unused.c:25: warning: `func2' defined but not usedtest_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.ctest_uninitialized.c: In function `test':test_uninitialized.c:6: warning: `x' might be used uninitialized in this function
2、非-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.ctest_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.ctest_shadow.c: In function `test':test_shadow.c:9: warning: declaration of `i' shadows a parametertest_shadow.c:10: warning: declaration of `g' shadows a global declarationtest_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.ctest_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.ctest_cast_qual.c: In function `test':test_cast_qual.c:10: warning: cast discards qualifiers from pointer target type
[-Wcast-align]这是个非常有用的选项,特别是对于在Solaris这样的对内存对齐校验的平台尤其重要。它用于在从对齐系数小的地址(如char*)转换为对齐系数大的地址(如int*)转换时给出警告。
e.g./* * test_cast_align.c */#include 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.ctest_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 */#include
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.ctest_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.ctest_unreachable.c: In function `test':test_unreachable.c:6: warning: comparison is always true due to limited range of data typetest_unreachable.c:9: warning: will never be executed
[-Wconversion]由于原型定义而引起的定点和浮点数之间的隐式转换(强制转换)或者由有符号数和无符号数之间隐式转换转换引起的警告。
e.g./* * test_conversion.c */#include
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.ctest_conversion.c: In function `main':test_conversion.c:15: warning: negative integer implicitly converted to unsigned typetest_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'。
[注1]本文中的例子的测试环境为Solaris 9 SPARC平台,GCC-3.2和Windows XP Intel x86平台,mingw32 gcc3.4.2,如无特殊差异,所有注释均针对这两个测试环境。
关于内联函数支持:
大家都知道,在程序中,通过把一个函数声明为内联(inline)函数,就可以让gcc把函数的代码集成(嵌入)到调用该函数的代码中去。这样处理可以去掉函数调用时进入/退出时间开销,从而肯定能够加快执行速度。因此把一个函数声明为内联函数的主要目的就是能够尽量快速地执行函数体。
在gcc中,如果内联函数中有常数值,那么在编译期间gcc就可能用它来进行一些简化操作,因此并非所有内联函数的代码都会被嵌入到调用代码处。内联函数嵌入调用者代码中的操作是一种优化操作,因此只有进行优化编译时才会执行代码的嵌入。若编译过程中没有打开优化选项 "-O",那么内联函数的代码就不会被真正地嵌入到调用者代码中,而是作为普通函数来处理。
在gcc-4.1的手册中指示,需要使用一定的优化级别才能开启某些优化选项,针对内联函数的优化选项主要有:
'-fno-inline' 忽略代码中的 inline 关键字,该选项使编译器将内联函数以普通函数对待;等同无优化选项时的处理'-finline-functions' 编译器尝试将'简单'函数集成到调用代码处;如果所有对该函数的调用都被替换而集成在调用者代码中,而且该函数使用static声明了,则该函数就不再像平常那样被编译成汇编代码。具体什么方式,需要查询。必须在-O3选项下才开启'-fearly-inlining' 加速编译 默认可用'-finline-limit=N' gcc默认限制内联函数的大小,使用该选项可以控制内联函数的大小;默认值是600,可以设置如下几个值: max-inline-insns-single N/2 max-inline-insns-auto N/2 min-inline-insns 130 or N/4 max-inline-insns-rtl N
'-fkeep-inline-functions' 将声明为static以及inline的函数放进目标文件中,即使所有对该函数的调用都被集成在调用者代码中;该选项不影响使用extern inline声明的内联函数,该声明属于GNU c扩展。
声明一个函数为内联函数的方法:
inline int func(int *a){ (*a)++;}
函数中的某些语句用法可能会使得内联函数的替换操作无法正常进行,或者不适合进行替换操作。例如使用了可变参数,内存分配函数 malloc(),可变长度数据类型变量,非局部 goto语句以及递归函数。编译时可以使用选项 -Winline 让 gcc 对标志成 inline 但不能被替换的函数给出警告信息以及不能替换的原因。如下面例子,它使用了可变长度数据类型变量作为参数:
inline int func(int *a){ int c = 4; char p[c]; /* 可变长度数组 */ (*a)++;}/* * 测试函数: 使用 gcc -Winline ... 来提示信息 */int main(void){ int d = 1; func(&d); return 0;}
假定存储文件为inline.c, gcc -O -Winline -c -o inline.o inline.c 来编译,这里 -O 选项必须打开,否则将没有警告信息输出,因为该函数会被当作普通函数来处理;警告信息如下:
inline.c: In function ‘func’:inline.c:2: warning: function ‘func’ can never be inlined because it uses alloca (override using the always_inline attribute)inline.c: In function ‘main’:inline.c:2: warning: inlining failed in call to ‘func’: function not inlinableinline.c:11: warning: called from here
可以看出它覆盖了 always_inline 属性,其它无法内联的的用法大家可以自己编写代码测试。 当在一个函数定义中既使用 inline 又使用 static 关键字时,那么如果所有对该内联函数的调用都被替换而集成在调用者代码中,并且程序中没有引用过该内联函数的地址,则该内联函数自身的汇编代码就不会被引用。这时,除非在编译过程中使用选项'-fkeep-inline-functions',否则 gcc 就不会再为该内联函数自身生成实际的汇编代码。由于某些原因,一些对内联函数的调用并不能被集成到函数中去。特别是在内联函数定义之前的调用语句是不会被替换集成的,并且也都不能是递归定义的函数。如果存在一个不能被替换集成的调用,那么内联函数就会像普通函数一样被编译成汇编代码,对于程序中有引用该内联函数的地址的处理同样无法集成。
对于上面这句话的理解,同样我们可以使用上面的 inline.c 函数来测试,当没有加上 static 关键字的时候,可以使用gcc -O -Winline -S -o inline.s inline.c 来生成汇编程序,可以看到 func 内联函数在汇编代码中同样被生成汇编代码而且被声明为函数;修改 inline.c 增加 static 关键字,gcc -O -Winline -S -o inline2.s inline.c 比较两个文件可以看到 inline2.s 中只有 main 符号,func 的代码直接被集成到 main 中了,此时如果想产生和没有加 static时的效果,编译时就要加上选项 '-fkeep-inline-functions';但是在 C++ 中,该选项会生成一个弱".weak"函数,也就是单独的汇编代码,若不加该选项,内联函数语义等同于 ISO C99 的语义,也就是都不单独生成汇编代码。
/* 不生成单独的汇编代码的版本,或者替换 static 为 extern */static inline int func(int *a){/* int c = 4; char p[c]; /* 可变长度数组 */*/ (*a)++;}
如果内联函数定义时没有使用 static,那么 gcc 就会假设其它程序文件中也对这个函数有调用。因为一个全局符号只能被定义一次,所以该函数就不能在其它源文件中再进行定义。因此这里对内联函数的调用就不能被替换集成。所以,一个非静态的内联函数总是会被编译出自己的汇编代码来。另外,ISO 标准 C99 中对不使用 static 关键字的内联函数定义等同于这里使用 static 的语义,但是为了保持兼容,最好还是明确指定 static 关键字。
如果在定义一个函数时还指定了 inline 和 extern 关键词,那么该函数定义仅用于内联集成,并且在任何情况下都不会单独产生该函数自身的汇编代码,即使明确引用了该函数的地址也不会产生。这样的一个地址会变成一个外部引用,就好像你仅仅声明了函数而没有定义函数一样。这种用法几乎等同于一个宏定义
优化:
gcc默认提供了5级优化选项的集合:
-O0:无优化(默认)
-O和-O1:使用能减少目标文件大小以及执行时间并且不会使编译时间明显增加的优化.在编译大型程序的时候会显著增加编译时内存的使用.
-O2:包含-O1的优化并增加了不需要在目标文件大小和执行速度上进行折衷的优化.编译器不执行循环展开以及函数内联.此选项将增加编译时间和目标文件的执行性能.
-Os:专门优化目标文件大小,执行所有的不增加目标文件大小的-O2优化选项.并且执行专门减小目标文件大小的优化选项.
-O3:打开所有-O2的优化选项并且增加 -finline-functions, -funswitch-loops,-fpredictive-commoning, -fgcse-after-reload and -ftree-vectorize优化选项.
-O1包含的选项-O1通常可以安全的和调试的选项一起使用:
-fauto-inc-dec -fcprop-registers -fdce -fdefer-pop -fdelayed-branch
-fdse -fguess-branch-probability -fif-conversion2 -fif-conversion
-finline-small-functions -fipa-pure-const -fipa-reference
-fmerge-constants -fsplit-wide-types -ftree-ccp -ftree-ch
-ftree-copyrename -ftree-dce -ftree-dominator-opts -ftree-dse
-ftree-fre -ftree-sra -ftree-ter -funit-at-a-time
以下所有的优化选项需要在名字前加上-f,如果不需要此选项可以使用-fno-前缀
defer-pop:延迟到只在必要时从函数参数栈中pop参数;
thread-jumps:使用跳转线程优化,避免跳转到另一个跳转;
branch-probabilities:分支优化;
cprop-registers:使用寄存器之间copy-propagation传值;
guess-branch-probability:分支预测;
omit-frame-pointer:可能的情况下不产生栈帧;
-O2:以下是-O2在-O1基础上增加的优化选项:
-falign-functions -falign-jumps -falign-loops -falign-labels
-fcaller-saves -fcrossjumping -fcse-follow-jumps -fcse-skip-blocks
-fdelete-null-pointer-checks -fexpensive-optimizations -fgcse
-fgcse-lm -foptimize-sibling-calls -fpeephole2 -fregmove
-freorder-blocks -freorder-functions -frerun-cse-after-loop
-fsched-interblock -fsched-spec -fschedule-insns
-fschedule-insns2 -fstrict-aliasing -fstrict-overflow -ftree-pre
-ftree-vrp
cpu架构的优化选项,通常是-mcpu(将被取消);-march,-mtune
Debug选项:
在gcc编译源代码时指定-g选项可以产生带有调试信息的目标代码,gcc可以为多个不同平台上帝不同调试器提供调试信息,默认gcc产生的调试信息是为gdb使用的,可以使用-gformat 指定要生成的调试信息的格式以提供给其他平台的其他调试器使用.常用的格式有
-ggdb:生成gdb专用的调试信息,使用最适合的格式(DWARF 2,stabs等)会有一些gdb专用的扩展,可能造成其他调试器无法运行.
-gstabs:使用stabs格式,不包含gdb扩展,stabs常用于BSD系统的DBX调试器.
-gcoff:产生COFF格式的调试信息,常用于System V下的SDB调试器;
-gxcoff:产生XCOFF格式的调试信息,用于IBM的RS/6000下的DBX调试器;
-gdwarf-2:产生DWARF version2 的格式的调试信息,常用于IRIXX6上的DBX调试器.GCC会使用DWARF version3的一些特性.
可以指定调试信息的等级:在指定的调试格式后面加上等级:
如: -ggdb2 等,0代表不产生调试信息.在使用-gdwarf-2时因为最早的格式为-gdwarf2会造成混乱,所以要额外使用一个-glevel来指定调试信息的等级,其他格式选项也可以另外指定等级.
gcc可以使用-p选项指定生成信息以供porf使用
GCC常用选项
选项 | 含义 |
---|---|
--help --target-help |
显示 gcc 帮助说明。‘target-help’是显示目标机器特定的命令行选项。 |
--version | 显示 gcc 版本号和版权信息 。 |
-o outfile | 输出到指定的文件。 |
-x language | 指明使用的编程语言。允许的语言包括:c c++ assembler none 。 ‘none’意味着恢复默认行为,即根据文件的扩展名猜测源文件的语言。 |
-v | 打印较多信息,显示编译器调用的程序。 |
-### | 与 -v 类似,但选项被引号括住,并且不执行命令。 |
-E | 仅作预处理,不进行编译、汇编和链接。如上图所示。 |
-S | 仅编译到汇编语言,不进行汇编和链接。如上图所示。 |
-c | 编译、汇编到目标代码,不进行链接。如上图所示。 |
-pipe | 使用管道代替临时文件。 |
-combine | 将多个源文件一次性传递给汇编器。 |
3 其他GCC选项
更多有用的GCC选项:
-
命令 描述 -l library
-llibrary进行链接时搜索名为library的库。
例子: $ gcc test.c -lm -o test-Idir 把dir加入到搜索头文件的路径列表中。
例子: $ gcc test.c -I../inc -o test-Ldir 把dir加入到搜索库文件的路径列表中。
例子: $ gcc -I/home/foo -L/home/foo -ltest test.c -o test-Dname 预定义一个名为name的宏,值为1。
例子: $ gcc -DTEST_CONFIG test.c -o test-Dname=definition 预定义名为name,值为definition的宏。 -ggdb
-ggdblevel为调试器 gdb 生成调试信息。level可以为1,2,3,默认值为2。 -g
-glevel生成操作系统本地格式的调试信息。-g 和 -ggdb 并不太相同, -g 会生成 gdb 之外的信息。level取值同上。 -s 去除可执行文件中的符号表和重定位信息。用于减小可执行文件的大小。 -M 告诉预处理器输出一个适合make的规则,用于描述各目标文件的依赖关系。对于每个 源文件,预处理器输出 一个make规则,该规则的目标项(target)是源文件对应的目标文件名,依赖项(dependency)是源文件中 `#include引用的所有文件。生成的规则可 以是单行,但如果太长,就用`\'-换行符续成多行。规则 显示在标准输出,不产生预处理过的C程序。 -C 告诉预处理器不要丢弃注释。配合`-E'选项使用。 -P 告诉预处理器不要产生`#line'命令。配合`-E'选项使用。 -static 在支持动态链接的系统上,阻止连接共享库。该选项在其它系统上 无效。 -nostdlib 不连接系统标准启动文件和标准库文件,只把指定的文件传递给连接器。 Warnings -Wall 会打开一些很有用的警告选项,建议编译时加此选项。 -W
-Wextra打印一些额外的警告信息。 -w 禁止显示所有警告信息。 -Wshadow 当一个局部变量遮盖住了另一个局部变量,或者全局变量时,给出警告。很有用的选项,建议打开。 -Wall 并不会打开此项。 -Wpointer-arith 对函数指针或者void *类型的指针进行算术操作时给出警告。也很有用。 -Wall 并不会打开此项。 -Wcast-qual 当强制转化丢掉了类型修饰符时给出警告。 -Wall 并不会打开此项。 -Waggregate-return 如果定义或调用了返回结构体或联合体的函数,编译器就发出警告。 -Winline 无论是声明为 inline 或者是指定了-finline-functions 选项,如果某函数不能内联,编译器都将发出警告。如果你的代码含有很多 inline 函数的话,这是很有用的选项。 -Werror 把警告当作错误。出现任何警告就放弃编译。 -Wunreachable-code 如果编译器探测到永远不会执行到的代码,就给出警告。也是比较有用的选项。 -Wcast-align 一旦某个指针类型强制转换导致目标所需的地址对齐增加时,编译器就发出警告。 -Wundef 当一个没有定义的符号出现在 #if 中时,给出警告。 -Wredundant-decls 如果在同一个可见域内某定义多次声明,编译器就发出警告,即使这些重复声明有效并且毫无差别。 Optimization -O0 禁止编译器进行优化。默认为此项。 -O
-O1尝试优化编译时间和可执行文件大小。 -O2 更多的优化,会尝试几乎全部的优化功能,但不会进行“空间换时间”的优化方法。 -O3 在 -O2 的基础上再打开一些优化选项:-finline-functions, -funswitch-loops 和 -fgcse-after-reload 。 -Os 对生成文件大小进行优化。它会打开 -O2 开的全部选项,除了会那些增加文件大小的。 -finline-functions 把所有简单的函数内联进调用者。编译器会探索式地决定哪些函数足够简单,值得做这种内联。 -fstrict-aliasing 施加最强的别名规则(aliasing rules)。 Standard -ansi 支持符合ANSI标准的C程序。这样就会关闭GNU C中某些不兼容ANSI C的特性。 -std=c89
-iso9899:1990指明使用标准 ISO C90 作为标准来编译程序。 -std=c99
-std=iso9899:1999指明使用标准 ISO C99 作为标准来编译程序。 -std=c++98 指明使用标准 C++98 作为标准来编译程序。 -std=gnu9x
-std=gnu99使用 ISO C99 再加上 GNU 的一些扩展。 -fno-asm 不把asm, inline或typeof当作关键字,因此这些词可以用做标识符。用 __asm__, __inline__和__typeof__能够替代它们。 `-ansi' 隐含声明了`-fno-asm'。 -fgnu89-inline 告诉编译器在 C99 模式下看到 inline 函数时使用传统的 GNU 句法。 C options -fsigned-char
-funsigned-char把char定义为有/无符号类型,如同signed char/unsigned char。 -traditional 尝试支持传统C编译器的某些方面。详见GNU C手册。 -fno-builtin
-fno-builtin-function不接受没有 __builtin_ 前缀的函数作为内建函数。 -trigraphs 支持ANSI C的三联符( trigraphs)。`-ansi'选项隐含声明了此选项。 -fsigned-bitfields
-funsigned-bitfields如果没有明确声明`signed'或`unsigned'修饰符,这些选项用来定义有符号位域或无符号位域。缺省情况下,位域是有符号的,因为它们继承的基本整数类型,如int,是有符号数。 -Wstrict-prototypes 如果函数的声明或定义没有指出参数类型,编译器就发出警告。很有用的警告。 -Wmissing-prototypes 如果没有预先声明就定义了全局函数,编译器就发出警告。即使函数定义自身提供了函数原形也会产生这个警告。这个选项 的目的是检查没有在头文件中声明的全局函数。 -Wnested-externs 如果某extern声明出现在函数内部,编译器就发出警告。 C++ options -ffor-scope 从头开始执行程序,也允许进行重定向。 -fno-rtti 关闭对 dynamic_cast 和 typeid 的支持。如果你不需要这些功能,关闭它会节省一些空间。 -Wctor-dtor-privacy 当一个类没有用时给出警告。因为构造函数和析构函数会被当作私有的。 -Wnon-virtual-dtor 当一个类有多态性,而又没有虚析构函数时,发出警告。-Wall会开启这个选项。 -Wreorder 如果代码中的成员变量的初始化顺序和它们实际执行时初始化顺序不一致,给出警告。 -Wno-deprecated 使用过时的特性时不要给出警告。 -Woverloaded-virtual 如果函数的声明隐藏住了基类的虚函数,就给出警告。 Machine Dependent Options (Intel) -mtune=cpu-type 为指定类型的 CPU 生成代码。cpu-type可以是:i386,i486,i586,pentium,i686,pentium4 等等。 -msse
-msse2
-mmmx
-mno-sse
-mno-sse2
-mno-mmx使用或者不使用MMX,SSE,SSE2指令。 -m32
-m64生成32位/64位机器上的代码。 -mpush-args
-mno-push-args(不)使用 push 指令来进行存储参数。默认是使用。 -mregparm=num 当传递整数参数时,控制所使用寄存器的个数。