gcc编译的过程


前言

  • GCC 仅仅是一个编译器,没有界面,必须在命令行模式下使用。通过 gcc 命令就可以将源文件编译成可执行文件。

  • 一个C/C++文件要经过预处理(preprocessing)、编译(compilation)、汇编(assembly)和链接(linking)等4步才能变成可执行文件,通常使用“编译”统称这4个步骤。


一、gcc 编译四步骤

在这里插入图片描述

二、gcc编译常用参数

-I 指定头文件所在目录位置
-c 只做预处理,编译,汇编。得到二进制文件
-g 编译时添加调试文件,用于 gdb 调试
-Wall 显示所有警告信息
-D 向程序中“动态”注册宏定义
-l 指定动态库库名
-L 指定动态库路径

三、文件后缀名对应表

后缀名类型
.cc源程序
.h预处理器文件
.cppc++源程序
.i预处理后的c文件
.ii预处理后的c++文件
.s汇编语言源程序
.o目标文件(Object file)
.a静态链接库文件(linux)
.so动态链接库文件(linux)
.lib静态链接库文件(windows)
.dll动态链接库文件(windows)

四、预处理

预处理是读取 c 源程序,对其中的伪指令(以 # 开头的指令,也就是宏)和特殊符号进行“替代”处理;经过此处理,生成一个没有宏定义、没有条件编译指令、没有特殊符号的输出文件。这个文件的含义同没有经过预处理的源文件是相同的,仍然是 C 文件,但内容有所不同。

预处理主要包括以下过程:

  • 将所有的 #define 删除,并且展开所有的宏定义
  • 处理所有的条件预编译指令,比如 #if #ifdef #elif #else #endif 等
  • 处理 #include 预编译指令,将被包含的文件插入到该预编译指令的位置。
  • 删除所有注释 “//”和”/* */”.
  • 添加行号和文件标识,以便编译时产生调试用的行号及编译错误警告行号。
  • 保留所有的 #pragma 编译器指令,因为编译器需要使用它们

下面我们将一端简单的C代码做示例。

/* --------普通的c代码----------*/
#include <stdio.h>
#define COUNTE 5

int main(int argc,char ** argv)
{
    for(int i=0; i <= COUNTE; i++) {
        printf("hello world!\n");
    }   

    return 0;
}

然后经过预处理:

gcc -E hello.c -o hello.i

其注释被删除,且宏也被替代之后删除,如下图所示。
在这里插入图片描述

五、编译

编译程序所要作的工作就是通过词法分析和语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码

所用到的工具为 cc1(它的名字就是 cc1, x86 有自己的 cc1 命令, ARM 板也有自己的 cc1 命令)。

 gcc -S hello.i -o hello.s

在这里插入图片描述
不同的编译器交叉编译同一个 hello.i 文件生成的汇编文件也不相同,这也是 C 语言可移植性的一种体现。

六、汇编

汇编过程实际上指把汇编语言代码翻译成目标机器指令的过程。对于被翻译系统处理的每一个 C 语言源程序,都将最终经过这一处理而得到相应的目标文件。目标文件中所存放的也就是与源程序等效的目标的机器语言代码。 目标文件由段组成。通常一个目标文件中至少有两个段:

  • 代码段(文本段):该段中所包含的主要是程序的指令。该段一般是可读和可执行的,但一般不可写;
  • 数据段:主要存放程序中要用到的各种常量、全局变量、静态的数据。一般数据段都是可读,可写,可执行的;

在 Linux 系统上一般表现为 ELF 目标文件(OBJ 文件),用到的工具为 as。 x86 有自己的 as 命令, ARM 版也有自己的 as 命令,也可能是 xxxx-as(比如 armlinux-as)。

gcc -c hello.s -o hello.o  

在这里插入图片描述
因为翻译成了计算机可以识别的二进制文件,因此乱码。

七、链接

汇编程序生成的目标文件并不能立即就被执行,其中可能还有许多没有解决的问题。例如,某个源文件中的函数可能引用了另一个源文件中定义的某个符号(如变量或者函数调用等);在程序中可能调用了某个库文件中的函数,等等。所有的这些问题,都需要经链接程序的处理方能得以解决。 链接程序的主要工作就是将有关的目标文件彼此相连接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体,也就是可执行程序。 根据开发人员指定的库函数的链接方式的不同,链接处理可分为两种:

  • 静态链接
  • 动态链接

用到的工具为 ld 或 collect2。

1、静态链接

静态链接的优点:
(1)装载速度很快,运行速度比动态链接快;
(2)只需要开发人员在开发机上有完整的.lib文件,不需要在用户机器上有完整的.lib文件,自完备

静态链接的缺点:
(1)可执行文件很大,并且相同代码很多,资源浪费

动态链接编译:

gcc hello.o -o hello -static

在这里插入图片描述

2、动态链接

动态链接的优点:
(1)可执行文件很小;
(2)适合大规模软件开发,开发过程耦合度小、独立,便于不同开发人员和开发组织开发;
(3)不同编程语言按照约定可以使用同一套.dll库;

动态链接的缺点:
(1)速度没有静态链接快;
(2)不具有自完备,如果用户机器中没有.dll文件,程序将无法运行并且报错

动态链接编译:

 gcc hello.o -o hello

在这里插入图片描述

八、查看具体过程

编译程序时,加上 -v 选项就可以看到这几个步骤。比如:

gcc hello.c -o hello -v

可以看到如下的信息:

Gnep@lpvm:~/test$ gcc hello.c -o hello -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/11/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 11.3.0-1ubuntu1~22.04.1' --with-bugurl=file:///usr/share/doc/gcc-11/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-11 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-11-aYxV0E/gcc-11-11.3.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-11-aYxV0E/gcc-11-11.3.0/debian/tmp-gcn/usr --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-serialization=2
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04.1) 
COLLECT_GCC_OPTIONS='-o' 'hello' '-v' '-mtune=generic' '-march=x86-64'
 /usr/lib/gcc/x86_64-linux-gnu/11/cc1 -quiet -v -imultiarch x86_64-linux-gnu hello.c -quiet -dumpbase hello.c -dumpbase-ext .c -mtune=generic -march=x86-64 -version -fasynchronous-unwind-tables -fstack-protector-strong -Wformat -Wformat-security -fstack-clash-protection -fcf-protection -o /tmp/ccJn089M.s
GNU C17 (Ubuntu 11.3.0-1ubuntu1~22.04.1) version 11.3.0 (x86_64-linux-gnu)
	compiled by GNU C version 11.3.0, GMP version 6.2.1, MPFR version 4.1.0, MPC version 1.2.1, isl version isl-0.24-GMP

GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/11/include-fixed"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/11/../../../../x86_64-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/lib/gcc/x86_64-linux-gnu/11/include
 /usr/local/include
 /usr/include/x86_64-linux-gnu
 /usr/include
End of search list.
GNU C17 (Ubuntu 11.3.0-1ubuntu1~22.04.1) version 11.3.0 (x86_64-linux-gnu)
	compiled by GNU C version 11.3.0, GMP version 6.2.1, MPFR version 4.1.0, MPC version 1.2.1, isl version isl-0.24-GMP

GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: e13e2dc98bfa673227c4000e476a9388
COLLECT_GCC_OPTIONS='-o' 'hello' '-v' '-mtune=generic' '-march=x86-64'
 as -v --64 -o /tmp/ccZUpiuk.o /tmp/ccJn089M.s
GNU汇编版本 2.38 (x86_64-linux-gnu) 使用BFD版本 (GNU Binutils for Ubuntu) 2.38
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/11/:/usr/lib/gcc/x86_64-linux-gnu/11/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/11/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/11/:/usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/11/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/11/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-o' 'hello' '-v' '-mtune=generic' '-march=x86-64' '-dumpdir' 'hello.'
 /usr/lib/gcc/x86_64-linux-gnu/11/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/11/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/11/lto-wrapper -plugin-opt=-fresolution=/tmp/cc1e5NmP.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -z now -z relro -o hello /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/11/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/11 -L/usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/11/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/11/../../.. /tmp/ccZUpiuk.o -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-linux-gnu/11/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/crtn.o
COLLECT_GCC_OPTIONS='-o' 'hello' '-v' '-mtune=generic' '-march=x86-64' '-dumpdir' 'hello.'

可以看到很多输出结果,我们把其中的主要信息摘出来:

cc1 hello.c -o /tmp/ccJn089M.s			# 预处理 + 编译
as -o /tmp/ccZUpiuk.o /tmp/ccJn089M.s	# 汇编
collect2 -o hello Scrt1.o crti.o crtbeginS.o ccZUpiuk.o crtendS.o crtn.o	# 链接

以上 3 个命令分别对应于编译步骤中的预处理 + 编译、汇编和链接, ld 被 collect2 调用来链接程序。预处理和编译被放在了一个命令( cc1)中进行的,可以把它再次拆分为以下两步:

cpp -o hello.i hello.c
cc1 hello.i -o /tmp/ccJn089M.s

我们不需要手工去执行 cpp、 cc1、 collect2 等命令,我们直接执行 gcc 并指定不同的参数就可以了。

九、很有用的选项

gcc -E hello.c // 查看预处理结果,比如头文件是哪个
gcc -E -dM hello.c > 1.txt // 把所有的宏展开,存在 1.txt 里
gcc -Wp,-MD,abc.dep -c -o hello.o hello.c // 生成依赖文件 abc.dep,后面 Makefile 会用
echo 'main(){}'| gcc -E -v - // 它会列出头文件目录、库目录(LIBRARY_PATH)

我的qq:2442391036,欢迎交流!

  • 4
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: obs-studio的编译过程是将源代码转换为可执行文件的过程。对于obs-studio而言,它的编译过程涉及多个步骤。 首先,需要将编写的源代码保存为合适的文件格式,通常是使用C++语言进行开发。在编写代码时,需要考虑到obs-studio的特定要求和功能需求。 其次,在编译过程中,需要使用编译器来将源代码转换为机器语言。obs-studio常用的编译器包括GNU编译器集合(GCC)和Clang。编译过程中,编译器会检查代码中的语法错误和逻辑错误,并生成相应的目标文件。 然后,将生成的目标文件与必要的库文件进行链接。obs-studio依赖于许多外部库,如FFmpeg、libobs和libobs-scripting。链接过程的目的是将这些库文件与目标文件进行连接,形成完整的可执行文件。 最后,进行测试和调试。通过测试和调试,可以发现并解决代码中的错误和问题,确保obs-studio能够正常运行并拥有所需功能。 总结来说,obs-studio的编译过程包括保存源代码、使用编译器进行编译、链接库文件以及进行测试和调试。这些步骤将源代码转换为可执行文件,使obs-studio能够顺利运行。 ### 回答2: obs-studio是一款开源的跨平台音视频录制和直播软件。下面将用300字中文回答obs-studio编译过程。 obs-studio的编译过程分为几个主要步骤: 1. 准备环境:首先需要安装相应的编译工具和依赖库。这些工具和库包括cmake、git、gcc等。安装完成后需要进行相关配置,如设置环境变量等。 2. 下载源代码:使用git从obs-studio的官方仓库下载最新的源代码。可以选择稳定版本或开发版,然后通过指定分支或标签来获取对应的代码。 3. 生成编译配置:使用cmake生成编译所需的配置文件。这些配置文件会指明编译器、依赖库路径、编译选项等。 4. 编译代码:运行指定的编译命令,如make或ninja。这将根据配置文件中的指示编译源代码,并生成对应的可执行文件。 5. 安装可执行文件:通过运行make install或ninja install命令,将编译生成的可执行文件和相关的资源文件复制到系统指定的目录中。 6. 设置环境:根据需要,可能需要进行一些环境的配置。比如,设置obs-studio的路径、输入输出设备的选择、直播平台的账号配置等。 综上所述,obs-studio的编译过程包括准备环境、下载源代码、生成配置、编译代码、安装可执行文件和设置环境等步骤。这样就能够成功地将obs-studio软件编译成可用的版本,以供音视频录制和直播等用途。 ### 回答3: OBS-Studio是一款开源的视频录制和直播软件,可以在Windows、Mac和Linux操作系统上使用。编译过程通常包括以下几个步骤: 1. 首先,需要准备编译所需的软件和工具。这包括安装C++编译器、CMake、Git和其他相关的库和依赖项。 2. 接下来,从OBS-Studio的官方GitHub页面上克隆或下载源代码。可以使用Git命令`git clone`或直接下载压缩文件。 3. 一旦源代码被获取,就可以开始配置编译环境。在命令行中导航到源代码的根目录,并执行CMake命令来生成Makefile文件。 4. 在生成的Makefile文件中,可以设置一些选项,如编译类型(Debug或Release)、安装目录等。 5. 确定好编译选项后,就可以运行Make命令进行编译了。这个过程可能需要一些时间,取决于计算机性能和源代码的规模。 6. 编译完成后,将生成可执行文件和相关的库文件。可以通过运行编译后的可执行文件来启动OBS-Studio。 总体来说,OBS-Studio的编译过程相对较简单,但需要一些基本的编译知识和工具的使用经验。通过正确配置编译环境,并按照编译流程进行操作,就可以成功地编译出OBS-Studio的可执行文件,并开始使用这款强大的视频录制和直播软件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

须尽欢~~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值