增加函数指针

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

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

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

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

我理解这是对的,还是我离开了?

的种类。 您假设函数将以与源代码相同的方式布局在内存中。 最有可能的是,它们不会 – 编译器通常会将它们全部转移到所有地方。

但是,你可以做的是使用指向当前指令的指针逐步执行代码,并将该计数器递增一定量以获得下一条指令。 但是,在这种情况下,我们不再将其称为函数指针 ,因为它不仅仅指向函数的开头; 相反,我们称之为指令指针 。

实际上,这正是计算机的工作原理 – 它有一个称为程序计数器的特殊寄存器 ,它始终指向当前指令,并在每条指令后将其递增一定量GOTO命令相当于将值写入程序计数器)

然而,在现实世界中,这不是调试器的工作原理 – 实际上,我甚至不确定是否有可能指向C中内存中代码段的指针,而不是函数指针 。 更有可能的是,如果您需要模拟程序计数器,例如为另一个处理器类型编写模拟器,则只需要使用此技术。

使用我设法追踪的草案C标准(N1124),我们有类似的规则。 关于加法表达式(第6.5.6 / 2节)的部分说明了这一点

另外,两个操作数都应具有算术类型,或者一个操作数应是指向对象类型的指针

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

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

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

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

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

但是,§3.9/ 9表明了这一点

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

总而言之,这意味着您无法在C ++中增加函数指针。

希望这可以帮助!

你可以(或者至少可以 )做这样的事情,但这显然是非平凡的。 首先,你不能实际增加或减少一个函数指针 – 它指向一个地址,但指针数学通常以sizeof(pointed to type)增量完成 – 但是使用一个函数,这是没有意义的,所以你不能做数学。

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

  1. 机器代码可以非连续存储。 编译器可以自由拆分或合并某些function(在优化中)
  2. 如果你手动增加一个指向函数的指针,你可能会进入函数的中间位置,这是错误的。
  3. 调试例程已经可用:您可以获取当前执行点的堆栈跟踪并解析堆栈中的执行指针所属的函数名称( man backtraceman backtrace_symbols )。 使用addr2line您可以将它们转换为行号。

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

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

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