C++ 编译过程

217 阅读3分钟

C++ 知识要点

C++ 的编译过程

编译的基本步骤

Step1:Call a preprocessor, like cpp. 预处理阶段,将各类include/宏展开;
Step2:Call an actual compiler, like cc or cc1; 编译阶段,语法检查,没问题以后生成汇编;
Step3:Call an assembler, like as.  汇编过程,将上面汇编代码转成二进制代码;
Step4:Call a linker, like ld; 将各个二进制代码进行链接,生成最终的程序;

preproccess(预处理)

预处理包括的内容大概有,复制粘贴#include文件内容,解析宏定义#define#if#ifend,以及常量的直接计算

#include 为例

#include的作用请看下例
假设目前项目存在 acc.hMath.cpp 文件

  • acc.h,仅有一个符号内容而已
}
  • Math.cpp,一个简单的乘法函数
int Mutliply(int a, int b)
{
	return a * b;
#include "acc.h"

上面这两段代码可以编译并且运行成功。由此可以看来#include的作用就是粘贴复制

不信的话我们可以看下预处理文件,visual studio 2022默认设置是不会生成预处理文件的,需要我们修改项目配置。右键项目,然后点击属性

image.png 对照图中内容进行配置,预处理到文件 这项改为 YES

image.png Ctrl + f7 编译一下,然后再到output目录中找到预处理文件 Math.i,不出意外 acc.h 中的内容被复制粘贴到了Math.cpp文件中

image.png

image.png 我们可以尝试更多案例,看看 #define 预处理之后是什么样的吧。语法 #define INTEGER int,表示会找到文件中 a 全部替换成 b,不要问我为什么写 INTEGER,如果你想的话你可以把 INTEGER 换成 “傻逼” 也是可以的

image.png

image.png 可以看到 预处理文件中的 INTEGER 全部被替换成了 int,同样在预处理阶段完成的事情还有很多,#if #ifend等等。

前面我们说到了,#include 就是复制粘贴,我们这里可以验证一下 main.cpp 文件。

main.cpp 文件内容

#include <iostream>

int main()
{
	std::cout << "Holle Word" << std::endl;
	std::cin.get();
}

预处理文件末尾内容,可以看到我么写的代码已经到了6w多行,前面都是 iostream 里面的代码

image.png

ok,现在我们来看一下obj文件内容。在此之前的 “预处理到文件” yes 设置改回 no。如果你设置了 yes, build将不会再产生 obj 文件。修改之后重新 ctrl + f7 编译生成obj文件

image.png 直接打开的话是二进制展示。很显然并没有什么卵用,我看不懂。

既然如此,得设置一下输出能看得懂的文件

动画.gif 按照上面动画设置,我们可以得到一个含有汇编内容的文件

; Listing generated by Microsoft (R) Optimizing Compiler Version 19.33.31629.0 

include listing.inc

INCLUDELIB MSVCRTD
INCLUDELIB OLDNAMES

msvcjmc	SEGMENT
__E68C42ED_Math@cpp DB 01H
msvcjmc	ENDS
PUBLIC	?Mutliply@@YAHHH@Z				; Mutliply
PUBLIC	__JustMyCode_Default
EXTRN	_RTC_InitBase:PROC
EXTRN	_RTC_Shutdown:PROC
EXTRN	__CheckForDebuggerJustMyCode:PROC
;	COMDAT pdata
pdata	SEGMENT
$pdata$?Mutliply@@YAHHH@Z DD imagerel $LN3
	DD	imagerel $LN3+64
	DD	imagerel $unwind$?Mutliply@@YAHHH@Z
pdata	ENDS
;	COMDAT rtc$TMZ
rtc$TMZ	SEGMENT
_RTC_Shutdown.rtc$TMZ DQ FLAT:_RTC_Shutdown
rtc$TMZ	ENDS
;	COMDAT rtc$IMZ
rtc$IMZ	SEGMENT
_RTC_InitBase.rtc$IMZ DQ FLAT:_RTC_InitBase
rtc$IMZ	ENDS
;	COMDAT xdata
xdata	SEGMENT
$unwind$?Mutliply@@YAHHH@Z DD 025051601H
	DD	01112316H
	DD	0700a0021H
	DD	05009H
xdata	ENDS
; Function compile flags: /Odt
;	COMDAT __JustMyCode_Default
_TEXT	SEGMENT
__JustMyCode_Default PROC				; COMDAT
	ret	0
__JustMyCode_Default ENDP
_TEXT	ENDS
; Function compile flags: /Odtp /RTCsu /ZI
;	COMDAT ?Mutliply@@YAHHH@Z
_TEXT	SEGMENT
math_a$ = 4
a$ = 256
b$ = 264
?Mutliply@@YAHHH@Z PROC					; Mutliply, COMDAT
; File E:\CppProject\cpp\cherno\Math.cpp
; Line 4
$LN3:
	mov	DWORD PTR [rsp+16], edx
	mov	DWORD PTR [rsp+8], ecx
	push	rbp
	push	rdi
	sub	rsp, 264				; 00000108H
	lea	rbp, QWORD PTR [rsp+32]
	lea	rcx, OFFSET FLAT:__E68C42ED_Math@cpp
	call	__CheckForDebuggerJustMyCode
; Line 5
	mov	DWORD PTR math_a$[rbp], 10
; Line 6
	mov	eax, DWORD PTR a$[rbp]
	imul	eax, DWORD PTR b$[rbp]
; Line 7
	lea	rsp, QWORD PTR [rbp+232]
	pop	rdi
	pop	rbp
	ret	0
?Mutliply@@YAHHH@Z ENDP					; Mutliply
_TEXT	ENDS
END

汇编无疑是比二进制的内容容易看懂的多,至于汇编内容这边就不做解释了,看得懂的都懂。

好了全篇内容到这里就结束了,下一遍我会讲一下 linker 的简要内容!!