gcc编译介绍

1. 编译步骤

编译步骤:预处理,编译,汇编,链接
1.预编译:gcc -E hello.c -o hello.i
2.编译:gcc -S hello.i -o hello.s
3.汇编:gcc -c hello.s -o hello.o
4.链接:gcc hello.o -o hello
-o是指定生成文件的的文件名

1.1 预处理

首先预处理是展开代码中所有的宏定义,展开代码中使用到的头文件,替换代码中所有的条件编译,删除代码中所有的注释、空行、空白。

1.2 编译

编译是检查代码中语法规范,这一阶段是消耗时间最长,消耗系统资源最多

1.3 汇编

将上面编译生成的汇编文件翻译成机器指令

1.4 链接

数据段合并地址回填,生成可执行文件

2. 常用编译参数

-I 指定头文件所在目录的位置,例如:创建一个hello.c和一个hello.h,创建一个inc文件夹,并且将hello.h放在inc文件夹下,这个时候去编译hello.c的时候就需要指定头文件的位置如果不指定就会出现以下错误:

vtlk:cc$gcc demo.c -o hello
demo.c:2:10: fatal error: demo.h: 没有那个文件或目录
 #include <demo.h>
          ^~~~~~~~
compilation terminated.

使用命令:

gcc -I ./inc demo.c -o hello

-c只编译,不链接成为可执行文件。编译器只是由输入的 .c 等源代码文件生成 .o 为后缀的目标文件,通常用于编译不包含主程序的子程序文件

-O对程序进行优化编译、链接。采用这个选项,整个源代码会在编译、链接过程中进行优化处理,这样产生的可执行文件的执行效率可以提高,但是编译、链接的速度就相应地要慢一些,而且对执行文件的调试会产生一定的影响,造成一些执行效果与对应源文件代码不一致等一些令人“困惑”的情况。因此,一般在编译输出软件发行版时使用此选项。
-O2比 -O 更好的优化编译、链接。当然整个编译链接过程会更慢

-pedantic 选项,当gcc在编译不符合ANSI/ISO C 语言标准的源代码时,将产生相应的警告信息

-Wall 选项,除了 -pedantic 之外,gcc 还有一些其他编译选项,也能够产生有用的警告信息。这些选项大多以 -W 开头。其中最有价值的当数 -Wall 了,使用它能够使 gcc 产生尽可能多的警告信息。

-Werror 选项,它要求 gcc 将所有的警告当成错误进行处理,这在使用自动编译工具(如 Make 等)时非常有用。如果编译时带上 -Werror 选项,那么 gcc 会在所有产生警告的地方停止编译,迫使程序员对自己的代码进行修改。只有当相应的警告信息消除时,才可能将编译过程继续朝前推进。

-g 和 -ggdb默认情况下,gcc 在编译时不会将调试符号插入到生成的二进制代码中,因为这样会增加可执行文件的大小。如果需要在编译时生成调试符号信息,可以使用 gcc 的 -g 或 -ggdb 选项。
gcc 在产生调试符号时,同样采用了分级的思路,开发人员可以通过在 -g 选项后附加数字1、2、3指定在代码中加入调试信息的多少。默认的级别是2(-g2),此时产生的调试信息包括:扩展的符号表、行号、局部或外部变量信息。
级别3(-g3)包含级别2中的所有调试信息以及源代码中定义的宏。
级别1(-g1)不包含局部变量和与行号有关的调试信息,因此只能够用于回溯跟踪和堆栈转储。
回溯追踪:指的是监视程序在运行过程中函数调用历史。
堆栈转储:则是一种以原始的十六进制格式保存程序执行环境的方法。

-l (小写的l)参数就是用来指定程序要链接的库,-l参数紧接着就是库名;放在/lib和/usr/lib和/usr/local/lib里的库直接用-l参数就能链接
-L参数跟着的是库文件所在的目录名。比如我们把libtest.so放在/aaa/bbb/ccc目录下,那链接参数就是-L/aaa/bbb/ccc -ltest
例如:

gcc -o hello hello.c -I /home/hello/include -L /home/hello/lib -lworld

/*
 -I /home/hello/include表示将/home/hello/include目录作为第一个寻找头文件的目录,寻找的顺序是:/home/hello/include-->/usr/include-->/usr/local/include 
-L /home/hello/lib表示将/home/hello/lib目录作为第一个寻找库文件的目录,寻找的顺序是:/home/hello/lib-->/lib-->/usr/lib-->/usr/local/lib
 -lworld表示在上面的lib的路径中寻找libworld.so动态库文件(如果gcc编译选项中加入了“-static”表示寻找libworld.a静态库文件),程序链接的库名是world
*/

-shared参数,编译动态库时要用到,比如gcc -shared test.c -o libtest.so

-fPIC 生成位置无关代码,代码无绝对跳转,跳转都为相对跳转,

  • 对于不加 -fPIC 生成的动态库,“ 生成动态库时假定它被加载在地址 0 处。加载时它会被加载到一个地址(base),这时要进行一次重定位(relocation),把代码、数据段中所有的地址加上这个 base 的值。这样代码运行时就能使用正确的地址了。”
  • 加 fPIC 选项,加上 fPIC 选项生成的动态库,显然是位置无关的,这样的代码本身就能被放到线性地址空间的任意位置,无需修改就能正确执行。通常的方法是获取指令指针的值,加上一个偏移得到全局变量 / 函数的地址。加 fPIC 选项的源文件对于它引用的函数头文件编写有很宽松的尺度。比如只需要包含个声明的函数的头文件,即使没有相应的 C 文件来实现,编译成 so 库照样可以通过。
  • 在内存引用上,加不加 fPIC 的异同;加了 fPIC 实现真正意义上的多个进程共享 so 文件。多个进程引用同一个 PIC 动态库时,可以共用内存。这一个库在不同进程中的虚拟地址不同,但操作系统显然会把它们映射到同一块物理内存上。对于不加 fPIC,则加载 so 文件时,需要对代码段引用的数据对象需要重定位,重定位会修改代码段的内容,这就造成每个使用这个 .so 文件代码段的进程在内核里都会生成这个 .so 文件代码段的 copy。每个 copy 都不一样,取决于这个 .so 文件代码段和数据段内存映射的位置。可见,这种方式更消耗内存。但是不加 fPIC 编译的 so 文件的优点是加载速度比较快

猜你喜欢

转载自blog.csdn.net/weixin_68294039/article/details/126940843