C语言 一个接受任何参数的函数指针可以与接受不同参数的函数兼容吗

7jmck4yq  于 11个月前  发布在  其他
关注(0)|答案(2)|浏览(140)

我在试图调和两件事。
我知道函数签名TYPE func(void);意味着函数接受 no arguments,这与:TYPE func();不同,TYPE func();意味着函数接受 any arguments(这是C与C++的区别)。
我还知道函数指针与具有不同参数(不同签名)的函数不兼容(如果我的措辞错误,请道歉)。
我读到过你可以把一个函数指针转换成不同的类型,然后再转换回来,然后你就可以调用这个函数了(也就是说,它可以“往返”工作)。
然而,我感兴趣的是当调用函数时,通过指针接受不同的参数会发生什么。我知道这可能福尔斯未定义的行为。但我觉得奇怪的是函数接受“任何参数”的概念。也许这种解释是错误的。
但我现在会用一个例子来解释。

#include <stdio.h>
#include <stdbool.h>

void (*func)(); /* accepts any arguments */

void func_1(int x);          /* 1 argument */
void func_2(int x, float y); /* 2 arguments */

enum MODE {PRINT_THEM, NOTE_THEM} mode;

int main(void)
{
    printf("Printing...\n");
    mode = PRINT_THEM;
    func_1(8);
    func_2(9, 10.0F);

    printf("Noting...\n");
    mode = NOTE_THEM;
    func = func_1;
    func(); /* function can have any arguments, so this one has 1, but it's not used */

    func = func_2;
    func(); /* function can have any arguments, so this one has 2, but they are not used */

    return 0;
}

void func_1(int x)
{
    if (mode == NOTE_THEM)   
        printf("func_1() has 1 argument: (int)\n");
    else /* mode == PRINT_THEM */
        printf("x == %d\n", x);
}

void func_2(int x, float y)
{
    if (mode == NOTE_THEM)   
        printf("func_2() has 2 arguments: (int, float)\n");
    else /* mode == PRINT_THEM */
        printf("x == %d, y == %f\n", x, y);
}

字符串
这里的总体思想是,当mode == PRINT_THEM时,你使用参数,因此参数很重要,你不能使用函数指针。
然而当mode == NOTE_THEM时,你 * 不 * 使用参数,因此它们的特定值无关紧要(它们可以是任何东西,甚至垃圾值也可以)。在这种情况下,由于参数无关紧要,理想情况下可以使用接受任何参数的函数指针(在这种情况下没有参数)。
我想避免的是被迫使用某种占位符参数,当它们不重要的时候。例如:

mode = NOTE_THEM;
func_1(placeholder_int);
func_2(placeholder_int, placeholder_float);


我能想到的唯一解决方案实际上根本不涉及函数指针,但是你可以使用C99复合字面量,并依赖于默认初始化,以便用正确的参数调用函数(满足编译器),但不关心它们的具体值或它们具体是什么。有点像这样:

mode = PRINT_THEM;
func_1( (FUNC_1_ARGS){8} );
func_2( (FUNC_2_ARGS){9, 10.0F} );

mode = NOTE_THEM;
func_1( (FUNC_1_ARGS){} );
func_2( (FUNC_2_ARGS){} );

sf6xfgos

sf6xfgos1#

你的问题就在一开始:
我知道函数签名TYPE func(void);意味着函数不接受任何参数,
是的
其不同于:TYPE func();
是的,在C23之前。从C23开始,TYPE func();TYPE func(void);的含义相同,就像它在C中的含义一样。
这意味着函数接受任何参数(这是C与C
的区别)。

。在C23之前的版本中,TYPE func()会宣告接受特定但 * 未指定 * 数目之参数的函式,这些参数属于特定但 * 未指定 * 的类型。宣告并未提供函式原型的事实,并不会免除呼叫者使用适当数目之参数呼叫具有适当类型的函式的责任。此外,在给定调用中提供的任何参数都服从于默认参数提升,而不是服从于到原型中给定的参数类型的赋值转换。

然而,我对通过指针调用接受不同参数的函数时会发生什么很感兴趣,我知道这可能福尔斯未定义的行为。
用原型定义的函数和不用原型定义的函数(K&R风格)的细节是不同的,但是调用一个带有与函数定义不兼容的参数列表的函数会显式地产生UB。
但我觉得奇怪的是函数接受“任何参数”的概念。
我想你的直觉对你很有帮助。这样的概念对C来说是陌生的,除了变元函数的形式,尽管其他一些语言支持它(Python,Bash,...)。
这里的总体思路是,当mode == PRINT_THEM时,使用参数,因此参数很重要,不能使用函数指针.
但是,当mode == NOTE_THEM时,不使用参数,因此它们的特定值无关紧要
就语言规范而言,这与是否使用参数无关。* 如果参数与函数不匹配,函数调用本身 * 将具有未定义的行为。您可以使编译器难以或无法进行编译时参数检查,并且可以影响在函数调用接口应用的参数转换。但是您不能逃避通过兼容的函数指针调用函数的要求,使用类型与函数定义正确匹配的参数。

bz4sfanl

bz4sfanl2#

一个接受任何参数的函数指针可以与接受不同参数的函数兼容吗
你的“接受任何参数的函数指针”实际上,正如C标准所描述的那样,指向一个没有原型的函数类型的指针。更准确地说,函数接受的参数是未知的(在类型中),而不是它接受任何参数。
function prototype 是一个函数的声明,它声明了它的参数的类型。
根据C 2018 6.2.7 3,没有原型的函数类型与具有原型的函数类型兼容。此外,指向此类类型的指针也是兼容的。然而,仅兼容性不足以使调用定义。在C 2018 6.5.2.2中,还有其他调用函数的规则。
你的代码中有一些调用使用了一个没有原型的类型来调用用原型定义的函数(参数类型列表)。这在6.5.2.2 6中有介绍:
如果表示被调用函数的表达式的类型不包括原型,则对每个参数执行整数提升,并且具有类型float的参数被提升为double。这些称为 * 默认参数提升 *。如果参数的数量不等于参数的数量,则行为未定义...
在这一段中还有一些额外的规则,我不在这里讨论,因为上面的规则已经足够了:实际函数定义中的参数数量不等于调用中的参数数量,因此行为不是由C标准定义的。
其中一个原因是,如果一个函数被调用的参数数量与它预期的不同,那么并不是所有的调用约定都有效。在现代系统中,我们通常会看到一个函数在通用寄存器中传递它的前几个整数或指针参数。如果你传递一个,两个或三个参数,你会把它们的值放在寄存器中,比如r3r4,和/或r5。如果不传递这些参数,寄存器将保持原样,并具有它们所具有的任何值,但计算机状态在其他方面是相同的。值得注意的是,堆栈帧中没有差异。然而,在旧系统中,被调用的函数可能会在返回时以某些方式清理堆栈。如果它有三个两字节参数,如果调用者实际上没有将六个字节压入堆栈,这种不匹配的弹出操作将损坏堆栈指针,程序执行将出错。
因此,C语言允许在函数类型上有一定的灵活性,但基本的规则是,调用函数的方式必须与函数的定义方式相匹配。

相关问题