初始化

记录技术与生活

C++ 函数调用跳转与typedef中const限定符的退化

今天写学校作业,题目中有涉及到函数指针的内容,闲来无事就多写了几个例子,结果发现了C++ 函数指针中一个比较有趣的现象:

我们先来分析一下以下代码能否正常运行:

首先,我们很明显的看到在typedef中对目标函数的签名和以上两个函数 sum 与 wrong_sub 都不符合,按理说构造这样的一个函数指针数组是会报错的。
可是我实验了一下,程序可以编译通过,上面的结果也都和对应的函数运行结果相同。

这就说明在这里:

  1. 要不然就是函数的参数中const限定符失效了
  2. 要不然就是typedef中关于目标函数参数的const修饰符没有生效

为了检查到底是哪里出现了问题,我开始对这段代码进行调试,首先下一个断点在函数结束处,然后观察 operators 数组中的变量:
《C++ 函数调用跳转与typedef中const限定符的退化》

在这里我们发现了两个奇怪的现象:

  1. 数组 operators 里面的两个函数实际的 Value 值与 Type 中的签名值并不相同 (Type 中带有 const 限制符,而实际值中 const 消失了)
  2. 当我们将两个函数的入口地址加入监视之后发现,两个指针指向的地址并不是函数的实际入口地址

从右边内存中可以看到,从地址 0x0034fd1c 开始有两个连续的指针在栈上,分别指向 0x008b119a 和 0x008b105a ,也就是左边监视列表中所对应的值。

那么这两个地址到底对应了什么呢?
我们进入反汇编查看:
《C++ 函数调用跳转与typedef中const限定符的退化》

很明显的,0x008b119a 这个地址的 sum 只有一条 jmp 的跳转,那么他跳转去了哪里呢?
0x008b2730 !!! 也就是我们真正 sum 函数的入口地址。

《C++ 函数调用跳转与typedef中const限定符的退化》

这是对应 0x008b2730 地址的 sum 函数,代码分配在代码常量区。

那么当我直接调用函数会发生什么呢?为了验证,我重新开始了程序,此处的sum函数地址如下:
《C++ 函数调用跳转与typedef中const限定符的退化》
这里 sum 函数地址在 0x013c3260

而在断点处所看到的汇编代码如下:
《C++ 函数调用跳转与typedef中const限定符的退化》

这里程序调用了 0x013c11ea 处的代码,那么这里又是什么呢?
由于不知道怎么用VS查看指定处的汇编代码,我用 OllyDbg 检查了以上地址的代码:
《C++ 函数调用跳转与typedef中const限定符的退化》

可以很明显的看到,在 0x013c11ea 处也仅仅是一条跳转指令,而目标地址就是我们的函数入口: 0x013c3260 !!!

基于以上的实验分析我们得出了两个结论:

  1. 无论是直接调用或者通过函数指针调用,编译器都会生成跳转向函数入口的一条 jmp 指令,并在执行这条指令之前进行一些其他准备操作
  2. 也正是因为如此,在 typedef 中对函数参数的 const 修饰符并不起作用,因为在编译期间编译器并不能检查到函数的内部指令,所以它会将这里的函数指针对应的所有参数 const 全部退化掉,所有在 typedef 中函数指针参数上的 const 就是个形式而已

在网上搜索到的很多帖子都说直接调用函数会直接 call 函数的入口地址,现在看来并不都是如此。
希望对大家有所帮助,我个人对汇编和C++也只是入门,有什么不对的地方还请大家指出,谢谢!

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.