C/C++ | 面试题每日一练 (1)

发布于:2025-02-19 ⋅ 阅读:(36) ⋅ 点赞:(0)

💢欢迎来到张胤尘的技术站
💥技术如江河,汇聚众志成。代码似星辰,照亮行征程。开源精神长,传承永不忘。携手共前行,未来更辉煌💥

C\C++ | 每日一练 (1)

题目

C 是否支持函数重载? C++ 是否支持函数重载?如果支持请简述函数重载的实现原理。

参考答案

C 不支持函数重载

C 语言中,函数的名称必须是唯一的。如果尝试定义两个同名的函数,即使参数列表不同,编译器也会报错。这是因为 C 语言的函数调用机制是基于函数名称的,编译器在编译时会根据函数名称生成对应的符号,而无法区分同名函数的不同版本。

例如,以下代码在 C 语言中是不合法的:

int add(int a, int b) {
    return a + b;
}

float add(float a, float b) {
    return a + b;
}

编译器编译时会报错,信息如下所示:

test.c:3:7: error: conflicting types for ‘add’; have ‘float(float,  float)3 | float add(float a, float b) {}
      |       ^~~
test.c:2:5: note: previous definition of ‘add’ with type ‘int(int,  int)2 | int add(int a, int b) {}
      |     ^~~

C++ 支持函数重载

C++ 语言支持函数定义多个同名函数,只要它们的参数类型或参数个数不同即可。C++ 编译器根据函数的名称、参数列表生成对应的函数符号(函数签名),从而可以区分同名函数的不同版本。

例如:

int add(int a, int b) {
    return a + b;
}

float add(float a, float b) {
    return a + b;
}

int add(int a, int b, int c) {
    return a + b + c;
}

底层机制

C/C++ 语言中,编译器会为每个函数生成一个唯一的符号名,并将其存储在符号表中。符号表是编译过程中用于记录程序中各种标识符(如变量、函数等)的名称、类型、作用域等信息的数据结构。

符号表的主要作用包括:

  • 标识符解析:帮助编译器在编译过程中解析标识符的含义,例如确定一个函数的返回类型和参数类型。

  • 链接:在链接阶段,链接器会根据符号表中的信息将不同模块中定义的函数和变量进行连接。

  • 调试信息:符号表还用于生成调试信息,帮助调试器在运行时找到函数的入口点、参数等。

C/C++ 语言中,函数调用的过程如下:

  • 编译阶段:首先编译器会解析源代码,根据函数声明生成唯一的函数符号,并叫符号存储到符号表中。对于后续的每个函数的调用,编译器会根据符号表中的信息生成对应的调用指令。另外如果某个函数在其他模块的中定义,编译器就会在符号表中记录一个未解析的引用,等待链接器在链接阶段处理。

  • 链接阶段:链接器会读取各个模块的符号表,将未解析的引用与实际的函数定义进行地址重定向,最终链接器会生成生成可执行文件。

  • 运行阶段:当程序运行时,操作系统会进行按需加载可执行文件到内存中,当触发到函数调用时,程序会根据链接器重定向后的函数地址跳转到对应的函数代码。

从以上的描述中可以看出,C/C++是否支持函数的重载就是看函数符号的生成机制,那么接下来给出同一个函数,分别对 CC++ 进行代码编译,查看各自函数符号生成的结果。

分别创建 test.ctest.cpp 代码文件,文件中添加函数如下所示:

int add(int a, int b) {
    return a + b;
}

分别编译 test.ctest.cpp 文件,生成目标文件 test.otestcpp.o,其中包含了关于add函数的代码和符号表条目。

执行编译命令,如下所示:

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

# 编译 test.cpp
g++ -c test.cpp -o testcpp.o

使用 nm 命令或者使用 objdump 命令查看两个 .o 文件,如下所示:

  • test.o 文件
$ nm test.o 
0000000000000000 T add

其中,0000000000000000 表示 add 函数的地址;T 表示这是一个代码段中的符号,通常是函数;add 表示的函数生成的函数符号。

  • testcpp.o 文件
$ nm testcpp.o 
0000000000000000 T _Z3addii

其中,0000000000000000 表示 add 函数的地址;T 表示这是一个代码段中的符号,通常是函数;_Z3addii 表示的函数生成的函数符号。

从以上的输出结果可知,对于 C++ 来说,编译器(如 GCC)对函数名进行修饰包含以下几部分:

  • _ZGCC 编译器对 C++ 函数名修饰的前缀。
  • 3:表示函数名的长度(这里是add,长度为3)。
  • add:函数的原始名称。
  • ii:表示函数的参数类型。i代表int,因此ii表示两个int类型的参数。

C++ 编译器为每个函数生成唯一的符号名,以便支持函数重载。

🌺🌺🌺撒花!

如果本文对你有帮助,就点关注或者留个👍
如果您有任何技术问题或者需要更多其他的内容,请随时向我提问。

在这里插入图片描述