声明回调函数时在C语言中参数数量错误会发生什么?

4

我正在为我所在的公司实现一项功能,但对于回调函数和函数指针有些疑惑。以下是示例代码:

struct callback {
    int (*func) (int *, int);
};

static struct callback cbstruct;

void install_func(struct callback *cbstruct, int (*func) (int *, int))
{
    cbstruct->func = func;
}

int write(int *integ)
{
    return *integ;
}

int main() {
    int * a = malloc(sizeof(a));
    *a = 5;

    install_func(&cbstruct, write);

    printf("%d\n", (cbstruct.func)(a,3));
    return 0;
}

正如您所看到的,这个程序使用函数指针为一个结构体注册回调。预期该函数接收两个参数(int*和int),但在示例代码中,“write”函数只接收了一个int*。

我本来以为会出现编译错误,但实际上只有一个警告:

funcpointer.c:24:26: warning: passing argument 2 of ‘install_func’ from incompatible pointer type
install_func(&cbstruct, write);
                          ^
funcpointer.c:10:6: note: expected ‘int (*)(int *, int)’ but argument is of typeint (*)(int *)’
void install_func(struct callback *cbstruct, int (*func) (int *, int))
     ^

这个程序似乎可以正确运行,在屏幕上打印出5,但考虑到定义和声明中参数数量的差异,我不确定它是否被正确编写。
为什么它可以编译并工作(表面上)?这样做是否存在即将发生的问题?我不确定从main函数调用的第二个参数(3)会发生什么。
我之所以问这个问题,是因为最近我改变了一个回调函数的签名(添加了一个参数),我期望从调用这个回调函数的地方看到许多错误,但我只得到了一些警告,程序继续(表面上)正常运行。
谢谢!

4
这段话的意思是:它能编译通过,因为它将一个函数指针类型转换为另一个函数指针类型。你应该修改你的函数,并且最好不要使用“write”来避免与标准库中的write函数发生冲突。 - George Houpis
1
什么是malloc(sizeof(a)); - ForeverStudent
2个回答

4
标准C11在§6.3.2.3中规定:
一个类型的函数指针可以转换为另一种类型的函数指针,然后再转回来;这个结果应该等同于原始指针。但是,如果使用转换后的指针来调用与引用类型不兼容的函数,则行为是未定义的。
因此,您可以在不同类型的函数指针之间进行转换,但明确指出,如果调用与引用指针不兼容的函数,则会导致未定义的行为。

2
这取决于不匹配的是哪一边。如果回调函数需要一个参数,但传递了两个参数,则回调函数将读取第一个参数并忽略第二个参数。由于调用方负责设置和撤销调用,因此没有问题。
但反过来可能会导致未定义的行为。如果回调函数期望有两个参数,但调用方只认为它需要一个参数,则第二个参数将未被设置,因此其值为“未定义”(即内存位置中的任何内容)。

3
只要调用约定允许调用者负责清除堆栈(例如__cdecl),第一个案例就不会导致UB,但在使用不同的调用约定(例如__stdcall)时就无法正常工作,因为此时被调用方负责堆栈清除。 - Jack

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