编译选项学习总结(原创,禁止转载)
基本选项
一般来讲,C/C++从源代码到可执行程序之间要经历四个步骤:
预处理:展开头文件/宏替换/去掉注释/条件编译
编译:检查语法,生成汇编
汇编:汇编代码转换机器码
链接:链接到一起生成可执行程序
-E:只进行预处理,不编译
执行指令
gcc -E test.c
命令行显示出预处理信息,但不生成文件
gcc -E hello.c > 1.txt
将相关预处理信息重定向到文件1.txt中,文件内容显示如下
typedef signed char __int8_t;
typedef unsigned char __uint8_t;
typedef short __int16_t;
typedef unsigned short __uint16_t;
typedef int __int32_t;
typedef unsigned int __uint32_t;
typedef long long __int64_t;
typedef unsigned long long __uint64_t;
typedef long __darwin_intptr_t;
typedef unsigned int __darwin_natural_t;
typedef int __darwin_ct_rune_t;
typedef union {
char __mbstate8[128];
long long _mbstateL;
} __mbstate_t;
typedef __mbstate_t __darwin_mbstate_t;
.
.
.
-S:只编译,不汇编
执行指令
gcc -S test.c
生成文件test.s,文件内容如下:
.section __TEXT,__text,regular,pure_instructions
.build_version macos, 11, 0 sdk_version 11, 3
.globl _main ## -- Begin function main
.p2align 4, 0x90
_main: ## @main
.cfi_startproc
## %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
subq $16, %rsp
movl $0, -4(%rbp)
leaq L_.str(%rip), %rdi
movb $0, %al
callq _printf
xorl %ecx, %ecx
movl %eax, -8(%rbp) ## 4-byte Spill
movl %ecx, %eax
addq $16, %rsp
popq %rbp
retq
.cfi_endproc
## -- End function
.section __TEXT,__cstring,cstring_literals
L_.str: ## @.str
.asciz "hello\n"
.subsections_via_symbols
表明此时根据源文件生成了汇编代码
-c:只编译、汇编,不链接
执行指令
gcc -c test.c
此时源文件经过编译和汇编直接生成二进制文件test.o,没有其他中间文件生成
-g:包含调试信息
执行指令
gcc -g test.c
可见不光生成了可执行文件a.out,同时还在当前文件夹下生成了具备调试信息的文件a.out.dSYM,此时可以使用调试工具gdb对可执行文件进行调试
-I:指定include包含文件的搜索目录
执行指令
gcc -I include/ -c test.c
表明在gcc执行过程中,除了标准库中的头文件,还可以从指定的目录去搜索头文件
-o:输出成指定文件名
执行指令
gcc -o b test.c
表明指定生成的二进制文件名称为b,之后运行程序b,和a.out运行是同样的结果
-lpthread:多线程编程
执行指令
gcc test.c -lpthread
指明gcc在链接阶段链接pthread库,从而编译包含多线程头文件的程序
-lm:程序使用了math.h中声明的库函数
执行指令
gcc test.c -lm
高级选项
-v:详细输出编译过程中所采用的每一个选项
执行指令
gcc -v
得到结果
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/4.2.1
Apple clang version 12.0.5 (clang-1205.0.22.11)
Target: x86_64-apple-darwin20.5.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
-C:预处理时保留注释信息
执行指令
gcc -E -C test.c
得到结果
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. The rights granted to you under the License
* may not be used to create, or enable the creation or redistribution of,
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
*
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
.
.
void clearerr(FILE *);
int fclose(FILE *);
int feof(FILE *);
int ferror(FILE *);
int fflush(FILE *);
int fgetc(FILE *);
int fgetpos(FILE * restrict, fpos_t *);
char *fgets(char * restrict, int, FILE *);
.
.
.
可见和单纯执行-E选项时相比,还多加了注释信息
-ggdb:在可执行文件中包含可供GDB使用的调试信息
-g选项和-ggdb选项只有细微的差别:
-g以OS本地格式(stabs,COFF,XCOFF或DWARF 2)产生调试信息。
-ggdb生成专门用于gdb的调试信息。
-fverbose-asm:在编译成汇编语言时,把C变量的名称作为汇编语言中的注释
执行指令
gcc test.c -S -fverbose-asm
得到结果
.section __TEXT,__text,regular,pure_instructions
.build_version macos, 11, 0 sdk_version 11, 3
.globl _main ## -- Begin function main
.p2align 4, 0x90
_main: ## @main
.cfi_startproc
## %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
subq $16, %rsp
movl $0, -4(%rbp)
movl $0, -8(%rbp)
movl $1, -12(%rbp)
leaq L_.str(%rip), %rdi
movb $0, %al
callq _printf
xorl %ecx, %ecx
movl %eax, -16(%rbp) ## 4-byte Spill
movl %ecx, %eax
addq $16, %rsp
popq %rbp
retq
.cfi_endproc
## -- End function
.section __TEXT,__cstring,cstring_literals
L_.str: ## @.str
.asciz "hello\n"
.subsections_via_symbols
-save-temps:自动输出预处理文件、汇编文件、对象文件,编译正常进行
执行指令
gcc -save-temps test.c
之后使用ls命令查看目录中文件状态
a.out test.bc test.c test.i test.o test.s
可见保留了所有的中间文件
-fsyntax-only:只测试源文件语法是否正确,不会进行任何编译操作
执行指令
gcc -fsyntax-only test.c
当源文件没有语法错误时,没有任何输出,当故意修改出一处语法错误时,得到结果
test.c:4:21: error: expected ';' at end of declaration
int i = 0, j = 1 printf("hello\n");
^
;
1 error generated.
显示出源文件语法中存在的错误
-llibrary:连接时搜索指定的函数库
例如:多线程编程例子
gcc test.c -lpthread
-Idirectory:指定额外的头文件搜索路径
例如:
gcc -I ../include test.c
添加目录../include为头文件搜索路径
-Ldirectory:指定额外的函数库搜索路径
例如:
gcc -L ../lib test.c
添加目录../iib为函数库搜索路径
-static:禁止使用动态库
详见下节
-shared:尽量使用动态库
详见下节
库的创建与使用
静态库
以创建静态库static_lib.a为例
1、编写C源文件static_lib.c,其中写入需要重复调用的函数,执行命令
gcc -c static_lib.c
生成目标文件static_lib.o
2、使用ar工具创建静态库
ar rcs static_lib.a static_lib.o
3、编写C头文件static_lib.h,其中写入这些函数的原型声明
4、编写主函数app.c,引入头文件static_lib.h,这样就可以正常使用那些自定义的可复用函数了
5、执行命令
gcc app.c -static ./static_lib.a -o app
编译生成可执行文件app
动态库
以创建动态库share_lib.so为例
1、编写C源文件share_lib.c,写入需要重复调用的函数,执行命令
gcc -shared -fPIC -o share_lib.so share_lib.c
生成动态库文件share_lib.so
2、编写C头文件share_lib.h,写入函数的原型声明
3、编写主函数app.c,引入头文件share_lib.h,之后就可以调用在动态库中自定义的函数了
4、执行命令
gcc app.c ./share_lib.so -o app
编译生成可执行文件app
出错提示选项
-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:如果在同一个可见域内某定义多次声明,编译器就发出警告,即使这些重复声明有效并且毫无差别。
gcc常见警告含义
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时有效;
其它选项
语言标准
-ansi:ANSI标准
-std=c99:C99标准
-std=gnu89:ISO/IEC 9899:1990 以及GNU扩充
-std=gnu99:ISO/IEC 9899:1999 以及GNU扩充
-trigraphs:支持ISO C三字符组
优化选项
-O0:关闭所有优化选项
-O1:第一级别优化,使用此选项可使可执行文件更小、运行更快,并不会增加太多编译时间,可以简写为-O
-O2:第二级别优化,采用了几乎所有的优化技术,使用此选项会延长编译时间
-O3:第三级别优化,在-O2的基础上增加了产生inline函数、使用寄存器等优化技术
-Os:此选项类似于-O2,作用是优化所占用的空间,但不会进行性能优化,常用于生成最终版本
自定义扩展名
-x:使用此选项可以指定自定义的源文件扩展名,类型有c、c-header、cpp-output、assembler、assembler-with-cpp、none
凡-x后面所列的所有文件都会被视为其指定的类型,要想改变类型可以再一次使用-x选项,或者使用-x none回到默认设置
示例:gcc -o test test.c -x assembler test.asm -x c test2.c