C++ 重载原理 以及 extern 关键字

目录

C++支持函数重载的原理 :名字修饰(name Mangling)

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

extern 关键字

【经典面试题】


C++支持函数重载的原理 :名字修饰(name Mangling)

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

  • 实际项目通常是由多个头文件和多个源文件构成。

        【假如a.cpp中调用了b.cpp中定义的Add函数时】

        执行编译后,在链接前,a.o 的目标文件中没有 Add 的函数地址

        因为 Add 是在 b.cpp 中定义的,所以 Add 的地址在 b.o 中。

  • 链接阶段就是专门处理这种问题

        链接器看到 a.o 调用 Add,但是没有 Add 的地址,就会到 b.o 符号表中找 Add 的地址,然后链接到一起。面对 Add 函数,链接接器会使用哪个名字去找呢?每个编译器都有自己的函数名修饰规则,这里采用的是Linux下的修饰规则。

        

  • 在Linux下 g++ 和 gcc 的修饰规则

        现在有一个 .c 文件:test.c

// test.c

void func(int a, double b, int* p)
{}

int main()
{
   func(1, 2, 0);
   return 0;
}

采用C语言编译器(gcc)结果

#执行编译
gcc -o test test.c

#查看生成的二进制文件 test 
objdump -S test

#执行后能看到 func 函数的地址及信息,例如:
00000000004004ed <func>:
...
...
...

采用C++编译器(g++)结果 

#执行编译
g++ -o test test.c

#查看生成的二进制文件 test 
objdump -S test

#执行后能看到 func 函数的地址及信息,例如:
00000000004005c1 <Z4funcidPi>:
...
...
...

可以看出

gcc的函数修饰后名字不变。

而g++的函数修饰后变成【_Z+函数长度+函数名+类 型首字母】。

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

通过这里就应该能理解C++支持函数重载,而C语言不支持函数重载。

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

extern 关键字

由于C和C++编译器对函数名字修饰规则的不同,在有些场景下可能就会出问题,比如:

  • C++中调用C语言实现的静态库或者动态库,反之亦然
  • 多人协同开发时,有些人擅长用C语言,有些人擅长用C++

在这种 混合模式 下开发,由于 和 C++ 编译器对函数名字修饰规则不同,可能就会导致链接失败,在该种场景下,就需要使用 extern "C" 。在函数前加 extern "C" ,意思是告诉编译器,将该函数按照C语言规则来编译。

// 创建一个C语言的静态库
//calc.h///
#pragma once
/*
 * 注意:
 * 在实现该库时,并不知道将来使用该静态库的工程是C语言工程还是C++工程
 * 为了能在C/C++工程中都能使用,函数声明时需加上extern "C"
 * 
 * __cplusplus:是C++编译器中定义的宏,即用该宏来检测是C工程还是C++工程
 * 
 * #ifdef __cplusplus
   extern "C"
   {
   #endif
        // 要导出函数的声明

   #ifdef __cplusplus
   }
   #endif
   作用:如果是C++工程,编译器已经定义_cplusplus宏,编译时该宏是可以被识别的,被声明的函数就被
 extern "C"修饰了,
   此时C++编译就知道,静态库中的函数是按照C的方式编译的,这样在链接时就会按照C的方式找函数名字
   如果是C工程,编译器未定义_cplusplus宏,编译时该宏无法被是被,则条件编译就无效,函数就不会被 
 extern "C"修饰
 */ 


#ifdef __cplusplus
 extern "C"
 {
 #endif
     int Add(int left, int right);

     int Sub(int left, int right);
 #ifdef __cplusplus
 }
 #endif


 //calc.c///
 #include "calc.h"
 int Add(int left, int right)
 {
     return left + right;
 }

 int Sub(int left, int right)
 {
     return left - right;
 }
【经典面试题】

Q: 下面两个函数能形成函数重载吗?有问题吗或者什么情况下会出问题?

 void Func(int a = 10)
 {
     cout<<"void Func(int a = 10)"<<endl;
 }

 void Func(int a)
 {
     cout<<"void Func(int a)"<<endl;
 }

A: 这两个函数不能实现重载,编译器会报错。

  • 函数重载要求函数的参数列表必须不同。
  • 然而,在代码中,这两个函数的参数列表是相同的(都只有一个 int 类型的参数)。
  • 虽然第一个函数提供了一个默认参数值 10,但默认参数不被编译器用来区分重载函数。
  • 因此,编译器会认为这两个函数是同一个函数,导致重定义错误
extern关键字在C语言中有多种用法。 第一种用法是在变量的声明中使用extern关键字,表示该变量在其他源文件中定义。例如,extern const char Buffer[];表示Buffer变量在其他文件中定义,并可以在当前文件中使用。 第二种用法是在函数的声明中使用extern关键字,表示该函数可能在其他源文件中定义。例如,extern int f();表示f函数可能在其他文件中定义,并可以在当前文件中使用。 第三种用法是在C++中使用extern "C"来指定函数的链接方式。这是为了解决C++函数重载带来的函数名和参数不同导致链接错误的问题。通过extern "C"修饰函数,告诉编译器保持函数名称不变,不生成用于链接的中间函数名。这通常在C++代码中与C函数交互时使用。 总而言之,extern关键字在C语言中用于声明变量和函数的链接方式,以及在C++中用于修饰函数以保持函数名称不变。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [C++全局变量的定义和声明](https://blog.csdn.net/webzhuce/article/details/38899635)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [C/C++extern关键字详解](https://blog.csdn.net/weixin_38218095/article/details/96473556)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值