编译过程的略讲

本文详细介绍了编程编译过程中的预处理、编译、汇编和链接环节,强调了C语言与C++在重载函数处理上的差异,并通过实例解析了这些概念。
摘要由CSDN通过智能技术生成

本次博文将会对编译过程中的预处理,编译,汇编,链接,做个个人的复习总结

准备代码

1,头文件

#pragma once
#include<iostream>
using namespace std;

void func(int a, char b);
void func(char b, int a);

2,函数文件

#define _CRT_SECURE_NO_WARNINGS 1
#include"Func.h"

void func(int a, char b) {
	cout << "func(int a,char b)" << endl;
}

void func(char b, int a) {
	cout << "func(char b,int a)" << endl;
}

3,测试文件

#define _CRT_SECURE_NO_WARNINGS 1
#include"Func.h"

int main() {
	func(1, 'a');
	func('x', 2);
}

在编译过程中,我们的第一步便是

一:预处理

过程通常包括:头文件展开        宏替换        条件编译        去掉注释……

头文件展开意味着将头文件的内容拷贝到Func.cpp里    和   test.cpp里的#include ” Func “中

注意:并不是把这些内容拷贝到cpp文件里,原因是,cpp文件是自己写的,编译器不能够擅自改变你的cpp

效果如图:

那么不把它拷贝到cpp文件中,它会变成什么呢?

答案是

而是生成新的文件Fun.i 和 test.i

Func.i里有func()函数的声明,也有func()函数的定义

test.i里有func()函数的声明,但没有func()函数的定义

生成了两个.i文件后就开始编译

二:编译

编译的主要作用是检查代码,生成对应的汇编代码

由预处理生成的.i 文件随后被C或C++编译器编译,生成汇编代码(.s 文件)。编译器在这个阶段进行语法和语义分析,优化代码,然后生成对应的汇编指令。

Func.i 生成 Func.s,下面截取一部分Func.s的代码

test.i 生成 test.s,下面截取一部分test.s的代码

那么我们便会看到在汇编码中,两个func函数都有其相应的地址,这些地址怎么来的?

我们这里看到的地址,是因为它完全链接(第四步)成功后找到的,并不是我在编译的时候给的

那你竟然没有地址,为什么编译器又会让我们通过呢?

是因为,在函数声明有了 func(int ,char)func(char, int)有了这两个声明,就相当于有了两个空头承诺,对编译器说 :没事,我最终会给你两个地址,你先继续编译下去

至于最后能不能通过,还得看以上两个承诺到最后能不能践行出来。

举个简单的例子吧

A国和B国原本是两个兄弟国家,后面B国听信谗言跟A国打起来了,但是B国远没有A国那么强大,就像C国借武器装备等,此刻空头承诺的两句声明(void func(int a, char b);
void func(char b, int a);)就相当于C国这么说

C国:没事,我有的是武器装备,你先打着,以后我会给你的。

B国:好嘞。

……

打着打着……

B国:哥们,武器呢?我快撑不住了

C国:抱歉,我的武器给别人了,不在我这了(即,没有Func函数中的函数)。

然后B国就炸了

这就是为什么,就算没有地址,程序也照样会先运行下去的原因

三,汇编

这一步要做的是

将汇编代码转换成二进制的机器码

Func.s→Func.o

test.s→test.o

.o文件是只有CPU才认识的,我们是不认识的

四,链接

将两个.o文件合并到一起,最后生成文件,至于什么文件,这就要看不同的OS了

  1. 可执行文件(Executable File):如果链接操作的目的是生成一个可以在操作系统上运行的程序,那么链接器会生成一个可执行文件。在不同的操作系统上,可执行文件的扩展名可能不同:

    • 在Windows上,通常是 .exe

    • 在类Unix系统(如Linux和macOS)上,通常是没有扩展名或者有时使用 .out

  2. 库文件(Library File):如果链接操作是为了生成一个库,供其他程序调用其中的函数或代码段,那么链接器会生成一个库文件。库文件的类型和扩展名也依赖于操作系统和库的类型:

    • 静态库(Static Libraries):在Windows上是 .lib,在类Unix系统上是 .a

    • 动态库(Dynamic Libraries):在Windows上是 .dll,而在类Unix系统上是 .so(Shared Object)。

  3. 位置无关代码文件(Position Independent Code Files):某些编译器提供了生成位置无关代码(PIC)的选项,这种代码可以被加载到内存中的任意位置而不需要修改。这通常用于创建动态库。生成的文件可能是 .o 文件,但这些文件包含了可以被链接器在不同地址处合并的代码。

  4. 其他中间文件:在某些情况下,链接过程可能会生成其他中间文件,例如,符号表文件(Symbol Files),这些文件包含了程序中所有符号(如函数和变量)的列表,以及它们在内存中的位置。

回到我们之前讲到那两个承诺:现在是应该要那两个承诺实现的时候了,

如果两个承诺不是骗人的,则链接成功,如果两个承诺是骗人的,则链接失败,现在来证明一下老赖

讲完了四个步骤,我们借此机会思考一下C语言为什么不能重载

C语言重载与C++重载原因

C语言中有一个符号表的东西,里面存着函数的名字跟地址(只记录函数名跟地址映射),如果C语言可以重载,那么当我拿着重名的函数去找符号表,我该对应哪个地址呢 ,所以这就是C语言不存在重载的原因

而C++支持重载的原因是,它不同于C语言用函数名来找地址,而是它会根据声明的函数性质来用命名规则重新命名这个函数,以达到就算你是重名函数,只要你的性质不一样,就有不同的新命名,所以我就能通过这个新命名找到正确的地址,从而进行下一步。

下面用Linux的指令来证明

图片总结

以上便是我对代码编译过程的浅度理解,如果有大佬斧正我的文章错误,将不胜感激,谢谢各位大佬们的阅读。

  • 24
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值