编译选项学习总结(原创,禁止转载)

基本选项

一般来讲,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

posted @ 2021-11-19 17:32  Zupernova  阅读(266)  评论(0编辑  收藏  举报