c++中函数重载的引入及引申出的一些问题分析

1. c++对c的兼容

c++ 在设计之初有一条重要的使命就是兼容c,所以我们写的c程序使用c++编译器也是可以编译通过的; 在一些特别的情况下编译不通过,也可以通过少量的修改使程序编译通过,我们可以认为c++ 在设计之初就是想成为c语言的超集,但这也是一把双刃剑,这个历史包袱也是c++ 如此庞大与复杂的原因之一。

注:即使c++编译器能够将c程序编译通过,也不代表他与c编译器编译出的程序执行结果是一样的,事实上c++编译器c编译器,对一些c语法的处理并不相同,本文并不会介绍c编译器c++编译器的所有不同。提出c++对c的兼容,目的是能使读者更好的理解本章内容。

2. c++对于函数重载的支持

2.1 什么是函数重载

  • C语言中不允许存在两个同名函数,C语言是不支持函数重载的。
  • c++ 针对 c语言进行了扩充,c++ 是允许在程序中存在同名函数的,c++ 是支持函数重载的。

    c++重载函数形参必须是不同的,要么类型不同,要么个数不同

2.2 C语言为什么不支持函数重载

  • 对如下函数使用gcc进行编译,查看汇编文件目标文件中的符号表:
    gcc -S hello.c -o hello.s	/* 只进行编译,生成汇编文件 */
    gcc -c hello.c -o hello.o	/* 进行编译和汇编, 生成可执行文件 */
    nm hello.o					/* 查看目标文件中的符号表 */
    
    void func1 (void)  {}
    void func2 (int a) {}
    void func3 (int a, int b) {}
    void func4 (int a, char b) {}
    int main (void)
    {
    	return 0;
    }
    
  • 符号表如下
    0000000000000000 T func1
    000000000000000b T func2
    0000000000000019 T func3
    000000000000002a T func4
    000000000000003d T main
    
  • 结论:
    C语言编译器, 将C语言编译为汇编之后, 直接使用函数名代表函数的地址,所以C语言中如果出现同名函数,编译器没办法办法让一个符号代表两个地址(这不符合汇编语法),链接时也会没有办法分辨应该链接哪个地址。所以c语言在语法层面就禁止出现同名函数。

2.3 c++支持函数重载的原因

  • 对如下函数使用g++进行编译,查看汇编文件目标文件中的符号表:

    注:在本文件中使用的四个函数名是相同的,使用gcc编译器是没办法编译通过的,使用g++ 则可编译通过。

    	g++ -S hello.c -o hello.s	/* 只进行编译,生成汇编文件 */
    	g++ -c hello.c -o hello.o	/* 进行编译和汇编, 生成可执行文件 */
    	nm hello.o					/* 查看目标文件中的符号表 */
    
    void func (void)  {}
    void func (int a) {}
    void func (int a, int b) {}
    void func (int a, char b) {}
    int main (void)
    {
        return 0;
    }
    
  • 符号表如下:

    000000000000000b T _Z4funci
    000000000000002a T _Z4funcic
    0000000000000019 T _Z4funcii
    0000000000000000 T _Z4funcv
    000000000000003d T main
    
  • 结论:
    c++在编译后并不直接使用函数名代表函数入口地址,而是依据形参的不同生成不同的符号,从而解决C语言函数不能重载的问题

c++编译后的符号命名规则我并没有研究,有兴趣可以自行查阅相关资料。

3.c++针对函数重载对c的兼容策略

3.1 问题引入

通过第二章我们可以看出来,c语言c++ 在二进制层面并不兼容,比如c++要链接c语言的库,假设 void func (void)是使用C语言编译器编译的库,那么void func (void)在目标文件中就是func,而c++ 调用void func (void) 则会找名为 _Z4funci 的符号,这是找不到的。所以c++c在二进制层面是不兼容的。

3.2 extern "C"的引入

c++ 引入 extern “C” 来解决上述问题,extern “C” 有两种使用场景:

  • 使用c编译器编译的库,在头文件中使用extern “C” 进行修饰,c++ 编译器在使用相关接口时就会采用,c的调用方式进行调用
  • 使用c++编译器想要编译出可供C语言调用的库,使用extern “C” 进行修饰,c++ 编译器就会采用c的方式对相关程序进行编译。

总结:我们可以这么理解,c++提供了一种机制extern “C”,使c++可以完全的兼容C语言。

3.3 g++ 对extern “C” 修饰的函数的编译示例

  • 编译的命令和代码
    g++ -S hello.c -o hello.s	/* 只进行编译,生成汇编文件 */
    g++ -c hello.c -o hello.o	/* 进行编译和汇编, 生成可执行文件 */
    nm hello.o					/* 查看目标文件中的符号表 */
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    void func1 (void) ;
    void func2 (int a);
    void func3 (int a, int b);
    void func4 (int a, char b);
    #ifdef __cplusplus
    	}
    #endif
    
    void func1 (void)  {}
    void func2 (int a) {}
    void func3 (int a, int b) {}
    void func4 (int a, char b) {}
    int main (void)
    {
    	return 0;
    }
    
  • 符号表如下
    0000000000000000 T func1
    000000000000000b T func2
    0000000000000019 T func3
    000000000000002a T func4
    000000000000003d T main
    

注:__cplusplus 是c++ 编译器中定义的宏,用来识别c++编译器。

你可能感兴趣的:(c++,开发语言,c语言)