C++支持函数重载的原理

目录

前言

 一.C/C++编译过程

1.预处理

1.1 打开步骤

 1.2预处理输入与输出

1.3预处理结果

2.编译

2.1进入编译

2.2编译步骤

2.3编译结果

3.汇编

3.1加载汇编文件

3.2 汇编过程

3.3汇编结果查看

4.链接 

 二.重载原理

2.1 C/C++语言编译的前提

2.2链接所需数据

2.3编译步骤

2.4编译查看(C)

2.5编译查看(C++)

 2.5C++函数名修饰

2.6编译结论


前言

C/C++编译过程是什么?

为什么C++支持函数重载,而C语言不支持函数重载呢?

在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接

接下来逐个进行详细介绍..........

 一.C/C++编译过程

1.预处理

预处理:在预处理阶段,预处理器处理源代码文件中的所有预处理指令,这包括宏定义的展开、条件编译指令的处理以及包含文件的插入。例如,#include <stdio.h>会被替换为该头文件的实际内容。此阶段结束后,会生成一个中间代码文件,通常以.i为扩展名。

以下是是代码和打开方式

1.1 打开步骤

 1.2预处理输入与输出

这里输入 gcc -E test.c  -o test.i    //cat test.i 是打开输出的test.i

使用gcc编译器对C语言源文件test.c进行预处理,并将预处理结果输出到test.i文件中。

具体解释如下:

    gcc: GNU编译器集合中的C语言编译器。
    -E: 表示只进行预处理操作,不进行编译和链接。
    test.c: 需要预处理的C语言源文件。
    -o test.i: 指定输出文件名为test.i。

执行该命令后,会生成一个名为test.i的文件,其中包含了预处理后的代码,可以用于进一步的编译和链接操作。

1.3预处理结果

2.编译

编译:编译阶段是编译器将预处理后的代码转换为汇编语言的过程。在这一阶段,编译器会检查代码的规范性和语法错误,并将高级语言翻译成机器可以理解的语言。这一步骤生成的汇编代码文件通常具有.s扩展名。

2.1进入编译

 这里输入gcc -S test.i -o test.s       //可以看出生成了test.s文件.

输入cat test.s查看生成的test.s文件

2.2编译步骤

将 test.i 文件转化成 test.s 文件,意味着你正在进行编译过程中的下一步,从预处理后的C代码(.i文件)到汇编语言代码(.s文件)。这个过程由GCC编译器的-S选项触发。下面是转换过程中发生的一些关键变化:

  1. 语法解析:GCC会解析.i文件中的预处理后的C代码,检查语法是否正确。

  2. 语义分析:编译器会进行语义分析,确保表达式和变量使用恰当,函数调用正确,等等。

  3. 中间代码生成:GCC通常会生成一种中间表示形式(例如GENERIC,GIMPLE或RTL),这是编译器内部使用的,以更接近机器语言但仍然与特定硬件无关的方式表示代码。

  4. 优化:在这个阶段,编译器可能会对中间代码进行各种优化,比如消除死代码,循环展开,常量折叠等。

  5. 目标代码生成:编译器将中间代码转换成特定于目标架构的汇编代码。

  6. 汇编代码输出:最终,GCC将汇编代码输出为.s文件。

在这个过程中,源代码逐渐被转换成计算机可以直接执行的机器码。.s文件包含了汇编指令,这些指令非常接近于机器码,但仍然是人类可读的。汇编语言代码通常需要通过汇编器(如as)进一步转换成二进制机器码(.o对象文件),最后链接器(如ld)将所有的对象文件链接成一个可执行文件或库。

2.3编译结果

3.汇编

汇编:汇编阶段把编译阶段产生的汇编代码转换成机器码,也就是二进制形式的指令。这些指令可以被计算机的处理器直接执行。汇编器将这些指令和数据组合在一起,生成目标文件,通常以.o为扩展名。

3.1加载汇编文件

 使用gcc -C test.s -o test.o 命令      //生成文件test.o,注意这是链接开始前的文件

也就是汇编完成后,链接开始前的文件

3.2 汇编过程

程序编译过程中的汇编阶段发生在编译器完成了源代码的语法分析和语义分析之后。这一阶段的输入通常是由编译器前端生成的汇编代码文件,输出则是对应的目标代码文件,即机器语言表示的程序。具体过程如下:

  1. 扫描(词法分析):在这一阶段,编译器的扫描器(Scanner)会读取源代码的字符序列,并将其分割成一系列的记号(Token)。这些Token是编程语言中具有特定意义的字符串序列,如关键字、标识符、常量和运算符等。
  2. 语法分析:语法分析器根据这些Token生成一个抽象语法树(AST),它代表了程序的结构。在这个阶段,编译器会检查源代码是否符合语言的语法规则,如果存在语法错误,编译器会报错并停止处理。
  3. 语义分析:编译器会检查AST中的语义信息,包括静态语义(如变量声明、类型检查)和动态语义(如表达式求值)。这个阶段确保了程序的正确性和合理性。
  4. 中间代码生成:在某些情况下,编译器会生成一个与目标机器无关的中间代码。这个中间代码是一种抽象的机器语言表示,它将被进一步转换成特定平台的机器代码。
  5. 代码优化:在生成中间代码后,编译器可能会对其进行优化,以提高程序运行的效率。优化可能涉及删除冗余代码、循环展开、变量重命名等技术。
  6. 目标代码生成:最后,编译器将中间代码转换成目标机器的机器代码。这个过程涉及到具体的寄存器分配、指令选择和指令调度等复杂工作。

总的来说,汇编过程是将高级语言编写的源代码转换成计算机可以直接执行的机器代码的过程。

3.3汇编结果查看

这里使用 objdump -d test.o  查看文件中的反汇编代码

4.链接 

连接器将多个目标文件以及所需的库文件连接起来,解决它们之间的引用关系,最终生成可执行文件,这个文件通常没有扩展名

链接过程可以细分为以下几个步骤:

  1. 地址解析和重定位:在编译阶段生成的目标文件(如.o.obj文件)中,可能会有一些未解析的地址引用,这些引用可能是对其他目标文件中定义的函数或变量的引用。链接器的任务之一就是解析这些地址,确保所有的引用都被正确地指向相应的定义。
  2. 符号解析:链接器会检查所有未定义的符号,并在其他目标文件或库文件中寻找它们的定义。如果找不到某个符号的定义,链接器将报错。
  3. 代码合并:链接器将各个目标文件中的机器代码合并到一个单一的可执行文件中。这个过程可能涉及代码的重新排列和优化,以提高最终程序的效率。
  4. 库文件的集成:如果程序使用了外部库,链接器需要将这些库中的代码也包含到最终的可执行文件中。这通常涉及到静态链接或动态链接的过程。
  5. 最终输出:完成上述步骤后,链接器将生成一个可执行文件,这个文件可以直接在操作系统上运行。

在实际开发中,为了简化编译和链接的过程,通常会使用集成开发环境(IDE)或者构建系统来自动处理这些复杂的步骤。例如,GCC编译器就提供了一条命令来完成整个编译和链接的过程。

 二.C++重载原理

2.1 C/C++语言编译的前提

C语言的编译过程通常包括预处理、编译、汇编和连接四个阶段。具体如下:

2.2链接所需数据

这里编辑了3给文件打开后就是这样,下图是代码组成:

2.3编译步骤

使用gcc -o  testc test.c list.c 生成链接文件

gcc -o  链接

testc 是这里生成的链接文件名

test.c list.c 是需要链接的两个文件

打开生成的.o文件;因为这里的.o文件重命名为testc。

2.4编译查看(C)

使用gcc编译(c语言)

结论:在linux下,采用gcc编译完成后,函数名字的修饰没有发生改变。

2.5编译查看(C++)

使用g++编译(C++)

结论:在linux下,采用g++编译完成后,函数名字的修饰发生改变,编译器将函数参 数类型信息添加到修改后的名字中。

 2.5C++函数名修饰

C++函数名的修饰(也称为名字改编或名字粉碎)主要发生在C++编译器在编译过程中,为了支持函数重载、模板实例化等特性,对函数名进行的修改。具体的规则如下:

  1. 编译器会在函数名前添加一些字符 :这些字符通常包括函数的返回类型、函数的参数类型以及调用约定(如cdecl, stdcall, fastcall等)。例如,如果你有一个名为foo的函数,其原型为int foo(double),那么在目标文件中,这个函数可能会被修饰为类似_foo_i_d的形式。

  2. 函数名会被编码为一种内部格式 :这种格式通常会包含函数的返回类型、函数的参数类型和数量等信息。例如,上述的_foo_i_d中,i可能表示返回类型为intd可能表示第一个参数的类型为double

  3. 不同的编译器可能会有不同的修饰规则 :例如,GCC和MSVC就有各自不同的名字修饰规则。

需要注意的是,由于这种名字修饰的存在,直接链接由不同编译器生成的目标文件可能会导致问题,因为同一个函数名可能会被修饰为不同的形式。这也是为什么在C++编程中,我们通常会避免直接链接由不同编译器生成的目标文件的原因。

2.6编译结论

1. 实际项目通常是由多个头文件和多个源文件构成,而通过C语言阶段学习的编译链接,我们知道【当前a.cpp中调用了b.cpp中定义的Add函数时】,编译后链接前,a.o的目标文件中没有Add的函数地址,因为Add是在b.cpp中定义的,所以Add的地址在b.o中。那么怎么办呢?

2. 所以链接阶段就是专门处理这种问题,链接器看到a.o调用Add,但是没有Add的地址,就 会到b.o的符号表中找Add的地址,然后链接到一起。

3. 那么链接时,面对Add函数,链接接器会使用哪个名字去找呢?

这里每个编译器都有自己的 函数名修饰规则。

所以C语言不支持重载,而C++支持重载(C++多了函数名修饰规则)

结论:在linux下,采用gcc编译完成后,函数名字的修饰没有发生改变

结论:在linux下,采用g++编译完成后,函数名字的修饰发生改变,编译器将函数参数类型信息添加到修改后的名字中。

以上就是本期补齐的内容,欢迎参考指正,如有不懂,欢迎评论或私信出下期!!!  

  • 15
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

锻炼²

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

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

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

打赏作者

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

抵扣说明:

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

余额充值