为什么将具有不同参数类型的函数存储到带有void *参数UB的函数指针中?

Yas*_*nub 4 c function-pointers

我最近偶然发现了一个有趣的问题(至少我认为是)。一个小例子:

#include <stdio.h>

typedef struct A {
    int x;
} A;

int (*func)(void*, void*);

int comp(A* a, A* b) {
    return a->x - b->x;
}

int main() {
    func = comp;
    A a;
    A b;
    a.x = 9;
    b.x = 34;
    printf("%d > %d ? %s\n", a.x, b.x, func(&a, &b) > 0 ? "true" : "false");
}
Run Code Online (Sandbox Code Playgroud)

我问自己以上显示的代码是否有效,但在编译时,GCC发出了警告:warning: assignment from incompatible pointer type。我进行了一些研究,有人在一个线程中说,上面的行为是不确定的,现在我很奇怪为什么是UB,因为void*可以将其保存为任何其他类型。只是标准的说法是“没有定义”,还是有一些可以解释的原因?我发现所有关于StackOverflow的问题都陈述了它的UB,但不完全是原因。也许与如何在内部取消引用函数指针有关?

dbu*_*ush 5

void *可以安全地将A 转换为其他任何类型,或从中转换为其他类型,但这不是您要尝试的转换。您正在尝试将转换int (*)(A *, A *)int (*)(void *, void*)。那是两件事。

的自动转换void *不适用于函数指针中的参数。为了使两个函数指针兼容,参数的数量和类型以及返回类型必须兼容。

原因之一是void *不需要与其他类型的指针具有相同的表示形式。当简单地转换为void *标准明确允许的a 和back 时,这很好,但是调用函数时可能会出现问题。

假设a void *用8个字节表示,而struct指针用4个字节表示。在您的示例中,将两个8字节的值压入堆栈,但将从堆栈中读取两个4字节的值作为函数中的参数。这将导致无效的指针值,该值随后将被取消引用。