当前位置:首页 > 技术文章 > 正文内容

对于普通成员函数,代码执行时,怎么跳转到代码区中对应的实现?

arlanguage1个月前 (04-01)技术文章22

在 C++ 中,普通成员函数的调用是通过 **编译时静态绑定** 实现的,其地址在编译阶段就已确定,并直接嵌入到生成的可执行文件的代码区(Text Segment)。当程序运行时,调用普通成员函数的过程本质上是 **直接跳转到代码区中已知的固定地址**。以下是详细的机制和步骤:


---


### 1. **编译阶段:确定函数地址**

- **函数代码的生成**:

普通成员函数的代码在编译时被编译成机器指令,存放在可执行文件的代码区。例如:

```cpp

class MyClass {

public:

void func() { /* 函数体 */ } // 编译后生成机器码,存放在代码区

};

```

- **符号绑定**:

编译器会为成员函数生成一个唯一的符号(例如 `MyClass::func`),并在编译阶段确定该符号的地址。这个地址在链接阶段(Linking)会被最终固定。


---


### 2. **调用时的底层转换**

- **隐式传递 `this` 指针**:

成员函数调用会被编译器转换为一个普通函数调用,并隐式添加 `this` 指针作为第一个参数。例如:

```cpp

MyClass obj;

obj.func(); // 编译器转换为:MyClass::func(&obj);

```

- **函数地址硬编码**:

编译器会将 `MyClass::func` 的地址直接写入调用指令中。例如,在 x86 汇编中:

```asm

; 伪汇编代码

lea ecx, [obj] ; 将 obj 的地址(this 指针)存入寄存器 ecx

call _ZN7MyClass4funcEv ; 直接调用代码区的函数地址(名称修饰后的符号)

```

- `_ZN7MyClass4funcEv` 是经过名称修饰(Name Mangling)后的函数符号,对应 `MyClass::func` 的地址。


---


### 3. **运行时执行流程**

- **代码区加载到内存**:

当程序启动时,操作系统的加载器会将可执行文件的代码区映射到进程的虚拟内存空间。此时,所有函数的地址(包括 `MyClass::func`)已被确定为固定的虚拟地址。

- **直接跳转到固定地址**:

当执行 `obj.func()` 时,CPU 的 `call` 指令会直接跳转到代码区中 `MyClass::func` 的固定地址,无需任何动态查找或计算。


---


### 4. **关键机制:静态绑定 vs 动态绑定**

- **静态绑定(普通成员函数)**:

函数地址在编译时确定,调用指令直接硬编码该地址,因此跳转是“直接”且“无额外开销”的。

- **动态绑定(虚函数)**:

需要通过虚函数表(vtable)间接查找函数地址,存在额外的查表开销(见下文对比)。


---


### 5. **对比:普通成员函数 vs 虚函数**

| **特性** | 普通成员函数 | 虚函数 |

|-------------------|-----------------------------|---------------------------|

| **绑定方式** | 静态绑定(编译时) | 动态绑定(运行时) |

| **函数地址确定** | 编译时硬编码到调用指令 | 运行时通过虚函数表查找 |

| **性能开销** | 无额外开销(直接跳转) | 有查表开销(间接跳转) |

| **底层指令** | `call <固定地址>` | `call [vtable + 偏移量]` |


---


### 6. **示例:从代码到汇编**

假设有以下代码:

```cpp

class MyClass {

public:

void func() {}

int data;

};


int main() {

MyClass obj;

obj.func();

return 0;

}

```


编译后的汇编代码(简化版):

```asm

; MyClass::func 的实现(代码区)

_ZN7MyClass4funcEv: ; 名称修饰后的符号

ret


main:

; 创建对象 obj(栈上分配)

sub esp, 4 ; 分配 4 字节(假设 int 大小为 4)

; 调用 obj.func()

lea ecx, [esp] ; 将 obj 的地址(this 指针)存入 ecx 寄存器

call _ZN7MyClass4funcEv ; 直接跳转到代码区的地址

; 返回

xor eax, eax

add esp, 4

ret

```


---


### 7. **为什么不需要对象存储函数地址?**

- **代码共享**:

所有同类对象共享同一份成员函数代码,无需每个对象单独存储函数地址。

- **静态绑定优势**:

函数地址在编译时已知,直接硬编码到调用指令中,避免了运行时的查找开销。


---


### 8. **总结:普通成员函数的跳转过程**

1. **编译阶段**:

函数代码被编译到代码区,地址固定。

2. **调用转换**:

编译器将 `obj.func()` 转换为 `MyClass::func(&obj)`,硬编码函数地址到调用指令。

3. **运行时执行**:

CPU 直接跳转到代码区的固定地址执行函数,隐式传递 `this` 指针。


这种机制高效且简单,是 C++ 高性能的基石之一。

扫描二维码推送至手机访问。

版权声明:本文由AR编程网发布,如需转载请注明出处。

本文链接:http://www.arlanguage.com/post/3763.html

分享给朋友:

“对于普通成员函数,代码执行时,怎么跳转到代码区中对应的实现?” 的相关文章

修改配置nginx限制无良爬虫频率

我们公司某些服务也经常被爬虫影响,例如贴吧在贴吧这个事故中,我是简单地匹配useragent,给它返回一个500的错误。今天看微博发现@金荣叶 的处理方法很灵活,可以动态设定一个爬虫的频率,达到减轻服务器负载,并且不至于封杀爬虫。#全局配置limit_req_zone $anti_spider zo...

一文说清nginx规则匹配(含案例分析)

概述: 在nginx中,匹配分很多情形,例如:区分大小写匹配、不区分大小写匹配、有强匹配开头、有模糊匹配某些字符、有匹配后重写动作、有匹配反向代理动作、有匹配后终止操作、还有匹配全局变量类型等等,在很多情形中,匹配动作也存在优先策略,来看看下面的案例。案例一、 server { .............

windows下的nginx安装和使用

.1 去官网下载相应的安装包:http://nginx.org/en/download.html1.2 解压后进入PowerShell(按住“shift”+“鼠标右键”)窗口,进入到nginx目录,输入start nginx.exe 进行nginx的安装安装成功后,在“任务管理器”中会显示“ngin...

PHP日志记录

背景在生产环境中日志的重要性显而易见,能快速定位问题和程序的调优。在LNMP架构中怎么记录好程序中的错误日志。设置error_log记录PHP日志信息#将会向PHP报告发生的每个错误 error_reporting = E_ALL #关闭页面显示才能将错误回写到日志文件 display_err...

5分钟搞懂nginx的location匹配规则

目录概述location介绍location指令语法location配置实例解析location常用场景实战禁止访问.sh后缀的文件实例php7进阶到架构师相关阅读概述这是关于php进阶到架构之Nginx进阶学习的第一篇文章:5分钟搞懂nginx的location匹配规则第一篇:5分钟搞懂nginx...

性能优化大揭秘:从代码到架构,全方位提升系统性能的实战技巧

在现代软件开发中,系统性能优化是一个永恒的话题。无论是移动应用、Web应用还是分布式系统,性能始终是用户体验、系统稳定性以及业务可扩展性的关键因素。性能瓶颈的存在不仅可能导致用户流失,还可能增加系统维护成本,甚至影响到业务的正常运行。因此,从代码层面到架构层面,全面的性能优化是每个技术人员必须掌握的...