为什么c++支持函数重载,c语言不支持

文章目录


前言

为什么c++可以支持函数重载,而c语言不支持,这其实是因为c++和c在链接时对函数名修饰规则的不同。


一、c++可以支持函数重载,而c语言不支持

1、c语言的函数名修饰规则

我们知道.c文件经过编译器编译为可执行程序需要经过以下的步骤。

其中在链接过程中,当test.c中调用了在add.c中定义的Add函数时,在进行汇编后,链接前。test.obj和add.obj文件中都生成了自己的符号表,我们可以看到test.obj目标文件的符号表中没有Add的函数地址,这是因为Add是在add.c中定义的,所以Add的地址在add.obj中,那么这种情况该怎么办呢?所以链接阶段就是专门处理这种问题,链接器看到test...obj中调用Add,但是符号表中没有Add的地址,就会到add.obj的符号表中找Add的地址,然后链接到一起。

那么链接时,面对Add函数,链接器会使用哪个名字去找呢?即链接器如果要找Add函数真正的地址,会直接拿Add这个名字去每个.obj目标文件中去找该函数的地址吗?这个问题其实就是为什么c++可以函数重载而c语言不可以函数重载的原因。结论就是c语言在编译后会直接按函数名去找该函数地址,而c++会使用函数名修饰规则为每个函数重新命名。

即例如如果在c语言写如下代码,定义了两个函数名相同但形参不同的函数Add时,在汇编后形成.obj的目标文件时,符号表中直接以Add函数的名字为符号。这样在链接时,链接器会直接拿Add这个函数名去每个.obj目标文件中找Add函数地址,然后就会发现有两个Add函数的地址,这样就会不知道链接哪一个Add函数地址,所以就会出现错误。

c 复制代码
#include<stdio.h>
int Add(int x, int y)
{
	return x + y;
}

double Add(double x, double y)
{
	return x + y;
}

int main()
{
	printf("%d\n", Add(1, 2));
	printf("%d\n", Add(2.3, 3.2));

	return 0;
}

现在我们在Linux系统下使用gcc编译器来编译test.c文件生成testc可执行文件。

objdump命令是Linux下的反汇编目标文件或者可执行文件的命令,我们使用objdump命令来查看testc的反汇编目标文件。我们可以看到在反汇编目标文件中Add函数和func函数没有进行函数名修饰,而是直接显示出来函数的实际名字。

2、c++的函数名修饰规则

而在c++中定义了两个函数名相同但形参不同的函数Add时,在汇编后形成.obj的目标文件时,在符号表中不会直接以Add函数的名字为符号,而是会根据函数的参数等不同而对函数名加以修饰。这样在链接时,链接器会拿修饰过的函数名去每个.obj目标文件中找该函数地址,这样就避免了一个函数名有两个函数地址。所以c++中可以进行函数重载。

例如我们使用下面的代码。然后使用g++编译器将test.c按照c++的语法来进行编译和链接形成可执行程序testcpp。

然后使用objdump命令查看可执行程序testcpp的反汇编文件。可以看到在testcpp的反汇编文件中Add函数名经过g++编译器的函数名修饰规则后变为Addii,而不是原来的函数名Add了。这样链接器就不会不知道找哪一个函数的地址了。

我们写一个函数重载的c++程序通过g++编译器编译过后,观察可执行文件的反汇编文件。可以看到根据Add函数的参数不同,Add函数经过函数名修饰规则产生的函数名也不同,这就是c++可以实现函数重载的原因。

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


3、总结

经过上面的分析就理解了C语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。如果两个函数函数名和参数是一样的,返回值不同是不构成重载的,因为调用时编译器没办法区分。

二、c++程序调用c写的静态库

具体实现

如果我们写了一个c++的项目,但是使用了一个c语言写的静态库,那我们该怎样用这个c的静态库呢。例如我们之前用c语言实现的栈的操作,然后此时我们想要在c++中调用这些栈操作,此时我们就需要做一些操作,然后才能使c++中可以使用这些c语言实现的库。

我们先将使用c语言实现的栈的操作打包为静态库。

然后在Debug文件夹下可以看到打包好的静态库,即.lib文件。

此时将该.lib文件和stack.h这个头文件都拷贝到我们的c++项目中。

然后在c++项目中打开属性进行一些c++项目静态库引入的配置。



此时该c++项目中就引入了test08.lib静态库,此时需要引入该静态库的头文件。

但是当我们运行时,会发现报出了错误。这是因为上面我们所讲的,c++和c语言在链接时,c语言在函数名修饰时是直接将函数名作为标识,而c++在函数名修饰时,会加上该函数的参数,共同组成该函数的标识。所以此时当c++调用c语言写的静态库时,在链接时会发现函数名修饰不一样,即c语言是按c语言的函数名修饰规则编译的这些函数名,而c++是按c++的函数名修饰规则编译这些函数名,所以在链接时会找不到这些函数,才会报出无法解析函数的错误。

此时需要告诉c++编译器,这个stack.h头文件中实现的方法都是使用c语言实现的,当编译时要按照c语言的函数名修饰规则编译,这样链接时才可以查到到这些函数,此时程序才不报错。

总结

这就是c++项目引用c语言实现的静态库。因为c++兼容c语法,所以静态库中的语法c++都兼容,但是c++和c语言在编译时对函数名修饰的规则不一样,所以需要做一些处理,以便c++在调用该静态库的方法时,可以找到这些方法。

三、c程序调用c++写的静态库

具体实现

c语言项目也可以调用c++写的静态库。不过也需要做一些处理。

此时先将上面的栈用c++的编译器打包成静态库,即将.c文件改为.cpp文件。

然后将打包好的静态库和头文件都拷贝到c项目的目录中。

此时运行c项目也会报错,错误原因还是因为c++和c语言在编译时函数名修饰规则不同,所以在链接时才查找不到对应函数。

此时在c++项目的.h文件中加入extern "C" {}语句,即告诉编译器在生成这些函数时,按照c的函数名修改来生成。此时会发现报错,这是因为extern "C" {}为c++语言,所以在c项目中会报错。

这时就要加上条件编译来判断了。即如果为c++文件,就加上c++的语法,如果不是c++文件,就不加上c++的语法。

总结

虽然c项目可以使用c++写的静态库,但是因为c语言不兼容c++语法,所以该c++库中不能出现c++的一些语法,不然c语言项目识别不了这些c++语法,就会报错。

相关推荐
若亦_Royi1 小时前
C++ 的大括号的用法合集
开发语言·c++
Aileen_0v03 小时前
【AI驱动的数据结构:包装类的艺术与科学】
linux·数据结构·人工智能·笔记·网络协议·tcp/ip·whisper
余额不足121383 小时前
C语言基础十六:枚举、c语言中文件的读写操作
linux·c语言·算法
Rinai_R4 小时前
计算机组成原理的学习笔记(7)-- 存储器·其二 容量扩展/多模块存储系统/外存/Cache/虚拟存储器
笔记·物联网·学习
吃着火锅x唱着歌4 小时前
PHP7内核剖析 学习笔记 第四章 内存管理(1)
android·笔记·学习
ragnwang4 小时前
C++ Eigen常见的高级用法 [学习笔记]
c++·笔记·学习
罗伯特祥5 小时前
C调用gnuplot绘图的方法
c语言·plot
胡西风_foxww5 小时前
【es6复习笔记】rest参数(7)
前端·笔记·es6·参数·rest
嵌入式科普6 小时前
嵌入式科普(24)从SPI和CAN通信重新理解“全双工”
c语言·stm32·can·spi·全双工·ra6m5
lqqjuly7 小时前
特殊的“Undefined Reference xxx“编译错误
c语言·c++