空指针和指向函数指针的指针之间的转换

Conversion between a void pointer and a pointer to function pointer

我知道 void * 函数指针无法安全地转换。

我的问题是下面的代码。

#include <stdio.h>
#include <stdlib.h>

void f(void *);

void g(void) {
    printf("hello world\n");
}

int main(void) {
    void (*p)(void) = g;
    void (**pp)(void) = malloc(sizeof(void (*)(void)));
    pp = &p;
    f(pp);
    free(pp); //segfault
    return EXIT_SUCCESS;
}

void f(void *p) {
    (*(void (**)(void))p)();
}

它在 -Wall -Wextra -pedantic -std=c99 的 gcc 中编译良好,但在程序调用 free 时在运行时失败。在这种情况下有什么问题?指向函数指针的指针不是数据指针吗?

事后思考:

正确的代码是这样的,

int main(void) {
    void (*p)(void) = g;
    f(&p);
    return EXIT_SUCCESS;
}

我不知道为什么我感到困惑:/

您的代码中存在错误:

你首先为一个函数指针分配space并将地址存储在pp中。

void (**pp)(void) = malloc(sizeof(void (*)(void)));

然后你立即用局部函数函数的地址覆盖它 p:

pp = &p;

您调用 f(),后者又调用传递地址处的函数指针。

f(pp);

最后您尝试 free 局部变量的地址,这是未定义的行为,您的环境会崩溃。

free(pp); //segfault

而不是 pp = &p;,您可能打算将 p 的值存储在分配的对象中:

*pp = p;

您关于转换 void * 和函数指针的评论是正确的:void * 指针不能安全地转换为 void(*)(),反之亦然。对象指针和函数指针可能有不同的不兼容表示。

在您的例子中,您将 void * return 值从 malloc() 隐式转换为指向函数指针 void (**)() 的指针。这种转换是正确的,因为 malloc 被定义为 return 适合存储任何对象类型的指针(假设分配了足够的 space)。

反过来,当将指针 pp 传递给 f 时,您将其隐式转换为 void *,后者又将其转换回 void (**)()。定义这样的转换是因为任何对象指针都可以安全地转换为 void * 并返回其原始类型。为了完整起见,所有函数指针都可以安全地从一种类型转换为另一种类型,只要在调用它之前将它们转换为引用函数的实际类型即可。