从函数指针读取数据是否合法?

4

按照标准,从函数指针访问数据是否合法?

比如:

#include <stdio.h>

int test(){
    //
}

int main() {
    int (*fp)(void) = &test;

    int i=0;
    for(i; i<10; ++i)
        printf("%x", *(fp+i));
}

在 ideone 上运行,看起来它运行正常 - 但我想知道这是否是预期的结果,或者是否实现定义,并且该页面可能会被操作系统保护?


2
它在哈佛架构处理器上无法工作,因为代码和数据有单独的地址空间。在x86上,我更喜欢您将那个指针转换为char*,因为函数不是对象,没有大小;您依赖于GCC的扩展,它将void*或函数指针上的指针算术定义为字节增量。至于页面保护,不用担心:从逻辑上讲,代码必须是可读的才能被执行(否则,您将如何知道要运行什么?)。 - Iwillnotexist Idonotexist
2
@我不存在我不曾存在 这是关于CPU访问内存的一个问题,它可能只被授予在指令处理的获取阶段而非执行阶段读取某些内存部分的访问权限。这里有一个有关“仅执行”保护的专利链接,我没有详细阅读过,但我猜想这是可能的。 - Diego
通常在可执行文件中,有只读数据段(.rodatar--)、读写数据段(.datarw-)和可执行代码(.textr-x)。 - Iwillnotexist Idonotexist
@Diego 这很有趣,但也很不可思议!这好像在鼓励通过机器代码的安全性来隐藏安全性。 - Iwillnotexist Idonotexist
@OllieFord 如果想要引起某人的注意,只需在他们的名字前加上 @ 符号,这样我就会收到通知。哈佛架构处理器因为使用不同的地址空间来存储代码和数据(甚至可能使用不同的指针格式和大小),所以它可以做很多事情。例如,对于 void foo(void){}int bar = 0;,可能会出现 (void*)&foo == (void*)&bar 的情况——尽管它们不在同一位置,但它们具有相同的地址。因此,您可能会尝试访问地址 A 处的 程序 内存,但实际上却访问了 数据 内存。 - Iwillnotexist Idonotexist
显示剩余2条评论
2个回答

4
从C标准的角度来看,不是的。
N1570中的J.5章节讨论了常见的扩展:
J.5 常用扩展 1.以下扩展在许多系统中广泛使用,但不能被所有实现所支持。包括任何可能导致严格符合标准的程序变得无效的扩展都会使一个实现不符合规范...
接着在J.5.7函数指针转换部分提到了这个扩展:
2.可以将一个指向函数的指针强制转换为一个指向对象或void的指针,从而允许检查或修改函数(例如,通过调试器)(6.5.4)。

1
接受此答案,因为它回答了我提出的问题 - 但我建议有兴趣的读者查看Iwillnotexist对OP的评论以获取更多信息。 - OJFord

-2

函数指针是经过设计合法的。

自己操作指针也是经过设计合法的,但可能会导致未定义的行为,包括程序崩溃。

我尝试将循环扩展到10000,结果出现了运行时错误,可能是因为内存访问被禁止了。


2
*(fp+i) 看起来像是一个解引用。 - Diego
1
我不确定关于“操纵指针会导致UB”,认为肯定缺少限定符? - OJFord
1
操纵指针并不一定会导致未定义的行为——这个说法太笼统了。 - Soren

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接