递增函数指针

Posted

技术标签:

【中文标题】递增函数指针【英文标题】:Incrementing function pointers 【发布时间】:2011-07-02 09:42:54 【问题描述】:

我刚刚了解了函数指针(指向存储函数机器代码的地址的指针)。这让我想到了机器代码以及它是如何存储在内存中的。

机器代码是否连续存储在内存中,以便可以“手动”增加指针,直到它指向下一个/上一个函数?

这是调试器的作用吗?他让我“看到”程序计数器在机器代码中指向的位置?

结论:可以用函数指针编程原始调试器吗?

我理解对了吗,还是我错了?

【问题讨论】:

【参考方案1】:

使用我设法找到的 C 标准草案 (N1124),我们有类似的规则。关于加法表达式的部分(§6.5.6/2)说

对于加法,要么两个操作数都应具有算术类型,要么一个操作数应为 指向对象类型的指针

并且对象类型在 §6.2.5/1 中定义为

存储在对象中或由函数返回的值的含义取决于用于访问它的表达式的类型。 (声明为对象的标识符是最简单的此类表达式;类型在标识符的声明中指定。)类型分为对象类型(完全描述对象的类型)、函数类型(描述函数的类型)和不完整类型(描述对象但缺少确定其大小所需信息的类型)。

由于函数类型与对象类型不同,这表明禁止对函数指针进行指针运算。

在 C++ 中,此操作是非法的。 §5.7/1 中给出的指针加法的定义如下:

另外,两个操作数都应具有算术或枚举类型,或一个操作数应是指向完全定义的对象类型的指针,而另一个应具有整数或枚举类型。

但是,§3.9/9 指出

对象类型是(可能是 cv 限定的)类型,它不是函数类型,不是引用类型,也不是 void 类型。

综合起来,这意味着您不能在 C++ 中递增函数指针。

希望这会有所帮助!

【讨论】:

我认为这个答案没有抓住重点——这是一个概念问题,而不是 “是否可以在 C 中增加函数指针?” 问题。他只是在滥用术语 function-pointer 来指代任何指向代码段的指针。 @BlueRaja:问题的标题正是如此,我认为值得一试。【参考方案2】:

您可以(或至少可以)做这样的事情,但这绝对不是微不足道的。首先,您实际上不能递增或递减函数指针——它指向一个地址,但指针数学通常以sizeof(pointed to type) 为增量完成——但对于函数,这没有意义,所以你无法对其进行数学运算。

大多数调试器(主要)通过使用将地址与行号、函数名、变量名等相关联的调试信息来工作。

【讨论】:

【参考方案3】:

有点。您假设函数将像在源代码中一样布置在内存中。最有可能的是,它们不会——编译器通常会不择手段地移动它们。

但是,您可以做的是使用指向当前指令的指针单步执行代码,并将该计数器增加一定量以到达下一条指令。但是,在这种情况下,我们将不再将其称为函数指针,因为它不仅仅是指向函数的开头;它还指向函数的开头。相反,我们将其称为instruction pointer。

事实上,这正是计算机的工作方式——它有一个特殊的register,称为program counter,它始终指向当前指令,并在每条指令后将其递增一定数量(a @ 987654325@命令相当于向程序计数器写入一个值).

然而,在现实世界中,这不是how debuggers work - 事实上,我什至不确定是否有可能在 C 中使用指向内存中的代码段的指针,而不是函数指针。更有可能的是,只有在需要模拟程序计数器时才需要使用这种技术,例如为另一种处理器类型编写模拟器。

【讨论】:

【参考方案4】:
    机器码可以不连续存储。编译器可以随意拆分或合并某些函数(在优化中) 如果手动增加指向函数的指针,可能会进入函数中间,这是错误的。 调试例程已经可用:您可以获得当前执行点的堆栈跟踪并解析堆栈中执行指针所属的函数名称(man backtraceman backtrace_symbols)。使用addr2line,您可以将它们转换为行号。

【讨论】:

【参考方案5】:

无法保证各个函数在内存中的位置。

函数本身将是一个连续的内存块(因为 CPU 按顺序执行指令),但如果启用代码优化,它可能与函数本身不同(指令可能会被大量重新排序)。它甚至可以从其他函数借用清理代码。

您可以编写一个原始调试器,但找出函数的结束位置并非易事。

【讨论】:

以上是关于递增函数指针的主要内容,如果未能解决你的问题,请参考以下文章

函数指针的用途

C++ Primer Plus基础知识部分快速通关

C语言习题练习5——指针

c++基本知识

函数中传递的变量引用不递增

指针的递增递减