视频推荐: 【c/cpp程序编译过程】https://www.bilibili.com/video/BV1JM4m127y7?vd_source=5a0790755035f26a67935abfbfcdfd5b
文章推荐: 详解C/C++代码的预处理、编译、汇编、链接全过程 - 知乎 (zhihu.com) C++ 预编译,编译,汇编,链接 - Suarezz - 博客园 (cnblogs.com) C/C++编译链接 - 知乎 (zhihu.com)(进阶)
关于编译器与不同系统的文件
一般编译器可以分为前端和后端,前端主要负责 语义分析,后端主要负责 代码生成
Linux | macOS | Windows | |
---|---|---|---|
可执行文件 | .out或/ | .out或/ | .exe |
目标文件 | .o | .o | .obj |
静态库 | .a | .a | .lib |
动态库 | .so | .dylib | .dll |
GCC
负责编译,生成各类库和目标文件,然后调用外部链接器进行链接然后生成可执行文件
LLVM
同时有编译器和链接器(例如:armclang
和armlink
)
运行程序的运行阶段,是让加载器将最后生成的可执行文件放到内存中
GCC、GNU、gcc与g++
GNU:一个操作系统,具体内容不重要,感兴趣可以参考:
GCC、GNU到底啥意思?_一只杨阳羊的博客-CSDN博客blog.csdn.net/qq_43617936/article/details/104504992
- GCC:GNU Compiler Collection(GNU编译器集合)的缩写,可以理解为一组GNU操作系统中的编译器集合,可以用于编译C、C++、Java、Go、Fortan、Pascal、Objective-C等语言
- gcc:GCC(编译器集合)中的GNU C Compiler(C 编译器)
- g++:GCC(编译器集合)中的GNU C++ Compiler(C++ 编译器)
简单来说,gcc调用了GCC中的C Compiler,而g++调用了GCC中的C++ Compiler
对于 *.c
和 *.cpp
文件,gcc分别当作 c 和 cpp文件编译,而g++则统一当作cpp文件编译
![[Pasted image 20241126223616.png]]
GDB(gdb)
GDB(gdb)全称“GNU symbolic debugger”,是 Linux 下常用的程序调试器。 为了能够使用 gdb 调试,需要在代码编译的时候加上-g
,如
|
|
常用指令
![[Pasted image 20241027090624.png]]
编译过程
![[Pasted image 20241027085312.png]]
![[Pasted image 20241027092346.png]]
step1. 预处理
![[Pasted image 20241026230159.png]] 命令:
|
|
作用:
- 去掉注释
- 预处理指令替换(ifndef ,宏定义等)
- include 导入的头文件替换
预处理实例对比
.cpp 文件 ![[Pasted image 20241026230758.png]]
预处理后的 .i 文件
还有替换的
|
|
被省略了,替换的内容有3万多行
翻到最下面查看
![[Pasted image 20241026230952.png]]
具体解释 预处理,顾名思义就是编译前的一些准备工作
预编译把一些#define
的宏定义完成文本替换,然后将#include
的文件里的内容复制到.cpp
文件里,如果.h
文件里还有.h
文件,就递归展开。在预处理这一步,代码注释直接被忽略,不会进入到后续的处理中,所以注释在程序中不会执行
step2. 编译阶段
![[Pasted image 20241027001928.png]]
命令:
|
|
对 .i 文件进行编译 也可以对 .cpp 文件进行编译
|
|
作用:
- 翻译成汇编语言
- 检查代码报错
具体解释
编译只是把我们写的代码转为汇编代码,它的工作是检查词法和语法规则,所以,如果程序没有词法或则语法错误,那么不管逻辑是怎样错误的,都不会报错。
编译不是指程序从源文件到二进制程序的全部过程,而是指将经过预处理之后的程序转换成特定汇编代码(assembly code)的过程
step3. 汇编阶段
![[Pasted image 20241027002224.png]] 命令:
|
|
作用:
- 将 .s 文件,汇编成二进制文件(二进制文件不可执行)
step4. 链接阶段
![[Pasted image 20241027002618.png]]
命令:
|
|
作用:
- 将目标文件 .o和库文件链接
具体解释:C语言代码经过编译以后,并没有生成最终的可执行文件(.exe 文件),而是生成了一种叫做目标文件(Object File)的中间文件(或者说临时文件)。目标文件也是二进制形式的,它和可执行文件的格式是一样的。对于 Visual C++,目标文件的后缀是.obj
;对于 GCC,目标文件的后缀是.o
。这就是一开始所说的编译完一堆.obj和.o文件的来源。
目标文件经过链接(Link)以后才能变成可执行文件。既然目标文件和可执行文件的格式是一样的,为什么还要再链接一次呢,因为编译只是将我们自己写的代码变成了二进制形式,它还需要和系统组件(比如标准库、动态链接库等)结合起来,这些组件都是程序运行所必须的。链接(Link)其实就是一个“打包”的过程,它将所有二进制形式的目标文件和系统组件组合成一个可执行文件。完成链接的过程也需要一个特殊的软件,叫做链接器(Linker)。
C++程序编译的时候其实只识别.cpp文件。每个cpp文件都会分别编译一次,生成一个.o或者.obj文件。这个时候,链接器除了将目标文件和系统组件组合起来,还需要将编译器生成的多个.o或者.obj文件组合起来。
g++
自动链接了系统组件,我们只需要把自定义函数的目标文件与main.o
链接即可
链接的其他过程
C/C++编译链接 - 知乎 (zhihu.com)(进阶)
合并段 ![[Pasted image 20241027092742.png]]
调整段偏移 ![[Pasted image 20241027092757.png]]
多文件g++编译指令
![[Pasted image 20241027222011.png]]
|
|
src/cmake_leran.cpp tools/hello.cpp 是在指定当前目录下哪些源文件需要编译 -I includes 是 库文件所在的目录
![[Pasted image 20241126230718.png]]
杂谈
关于库文件
![[Pasted image 20241126224636.png]]
![[Pasted image 20241126224803.png]]
关于可执行文件
![[Pasted image 20241126225616.png]]
关于C与C++联合生成可执行文件
在C++中,告诉编译器以C的标准进行编译 C/C++中的 extern 和extern“C“关键字的理解和使用(对比两者的异同)_c extern c-CSDN博客
|
|
|
|
如果编译器是 C++ 编译器,__cplusplus 宏会被定义,extern “C” 块将会被包含,这告诉编译器按照 C 语言的规则来处理函数名称,而不是 C++ 的规则。这样做的目的是为了保持与已经存在的 C 代码库的兼容性,特别是当使用 C++ 编写新代码时。
__cplusplus宏定义作用
在 C++ 中,__cplusplus 是一个预定义的宏,它用于确定正在使用的 C++ 标准的版本。这个宏的存在使得 C++ 代码能够以标准 C 形式输出,即以 C 的形式被调用,这对于跨平台编程和与 C 语言的兼容性至关重要。
宏的定义和使用
__cplusplus 宏通常在编写涉及 C 和 C++ 混合编程的头文件时使用。当 C++ 代码需要被 C 编译器调用时,使用这个宏可以确保 C++ 中的名称修饰(name mangling)不会发生,从而允许 C 代码安全地调用 C++ 函数。例如:
|
|
![[Pasted image 20241213212614.png]]
在这个例子中,如果编译器是 C++ 编译器,__cplusplus 宏会被定义,extern “C” 块将会被包含,这告诉编译器按照 C 语言的规则来处理函数名称,而不是 C++ 的规则。这样做的目的是为了保持与已经存在的 C 代码库的兼容性,特别是当使用 C++ 编写新代码时。
宏的值
__cplusplus 宏的值表示 C++ 标准的版本,例如:
- C++98 标准:199711L
- C++11 标准:201103L
- C++14 标准:201402L
- C++17 标准:201703L
- C++20 标准:202002L 不同的编译器,如 GCC、Clang 或 Visual C++,都遵循这些标准,并在编译时定义相应的 __cplusplus 值。这个宏的值可以用来确定编译器支持的 C++ 版本,从而在编写代码时做出相应的兼容性处理。
实际应用
在实际应用中,__cplusplus 宏确保了 C++ 代码可以与 C 代码无缝集成。例如,如果有一个用 C 语言编写的库,它的头文件是 f.h,产生的库文件是 f.lib,那么在 C++ 中使用这个库文件时,需要这样写:
|
|
这样,C++ 编译器就会知道如何正确地链接和调用 C 语言编写的函数。如果没有使用 __cplusplus 宏,可能会出现链接错误,因为 C++ 编译器和 C 编译器对函数名称的处理方式不同。
总结来说,__cplusplus 宏是 C++ 语言为了兼容 C 语言和支持混合编程而提供的一个重要工具。它允许开发者在保持 C++ 功能的同时,确保代码能够在不同的编程环境中正确运行。
关于extern关键字
C/C++中的 extern 和extern“C“关键字的理解和使用(对比两者的异同)_c extern c-CSDN博客 ![[Pasted image 20241213212619.png]]