为什么函数指针声明需要知道参数的类型和 return 值?

Why does a function pointer declaration need to know the types of the parameters and return value?

为什么 C 中的函数声明需要知道指向函数的参数类型和 return 值?

c中的一个指针声明是这样写的:

returnType ( *funcPtrName ) ( paramTypes )

例如下面的foo

char my_func ( int x )
{
    ...
}


int main ()
{
    char ( *foo ) ( int );

    foo = my_func;

    foo( 2 );
}

当声明一个指向值的指针时,类型用于确定一个元素的大小(以字节为单位)。例如在 int* p 中,int 告诉编译器 p 指向的元素是 sizeof( int ) 分开的。此信息用于指针运算。

但是,函数指针指向单个地址,不允许进行指针运算。

那么为什么需要额外的信息呢? returnTypeparamTypes 是否仅用于支持编译器的错误检查? (例如,当用户分配类型不匹配的函数时提醒用户)。

是的,你问的 "extra information" 主要是 "for the purpose of supporting error checking by the compiler."

函数原型带有参数类型信息,以便编译器可以检查通过函数指针的调用是否具有被调用函数的正确参数——无论它是什么。这就是函数原型的全部要点——确保使用正确的参数调用函数。

如果你对此不感兴趣(或者如果你在作弊并使用指向各种异构函数的函数指针,采用不同数量的参数,并希望一切顺利),你可以这样做-- 只需将括号留空,如本例所示:

#include <stdio.h>

int f1(int a, int b) { printf("f1(%d, %d)\n", a, b); }
int f2(double d) { printf("f2(%f)\n", d); }

int main()
{
    int (*allegedly_generic_function_pointer)();

    allegedly_generic_function_pointer = f1;
    (*allegedly_generic_function_pointer)(1, 2);
    allegedly_generic_function_pointer = f2;
    (*allegedly_generic_function_pointer)(3.4);
}

这里 allegedly_generic_function_pointer 是一个指向函数的指针,该函数采用未指定的参数,并且 returning int。 (在发明函数原型之前,"Function taking unspecified arguments" 是在 ANSI C 之前的版本中描述所有函数和函数指针的方式。)如图所示,您可以使用 allegedly_generic_function_pointer 指向并调用采用以下函数的函数不同的论点。 (不过,您的编译器可能会给您各种 "helpful" 警告。)

到目前为止,我一直在谈论参数信息。 return 类型的指向函数有一个更重要的目的——因此编译器可以生成正确的代码来处理 return 值。

(为什么 return 类型比参数类型更重要?好吧,当你在范围内调用一个没有原型的函数时,编译器假定预期参数的数量和类型与您在调用中实际提供的参数的数量和类型——编译器显然知道这一点,因为在编译调用时信息就在它的前面。但是它知道 return 类型的唯一方法被调用函数的类型是在函数指针类型声明中明确指定的。)

如果你想要一个指向函数的指针,该函数采用未指定的参数 以及未指定的 return 类型,这是你不能做的事情之一C.(为什么要这样的东西?问我写过一次的C解释器)

有几个原因。让我们首先考虑 return 值。

假设一个程序包含代码y = f(x);。在调用 f 之后,编译器需要获取它 return 编辑的值并将其赋值给 y。它从哪里获得这个价值?在某些系统中,整数 return 值在通用寄存器中传递,浮点 return 值在浮点寄存器中传递。所以编译器无法知道 return 值在哪里,除非它知道类型。宽整数,例如 long long int,可以在多个寄存器中传递。小型结构可能在寄存器中 returned,而大型结构可能在内存中 returned,使用指向 space 调用函数提供的指针,并且需要将其传递给被调用函数用作隐藏参数。

同样,编译器在调用函数时需要知道将参数放在哪里。前几个整数参数可能在通用寄存器中传递,而前几个浮点参数可能在浮点寄存器中传递。任何一种类型的附加参数都可能被压入堆栈。

此外,有时程序员可能会在函数实际需要浮点参数的地方传递整数表达式,例如 pow(x, 4)。当编译器知道参数必须是浮点参数时,它可以将其转换为预期的类型。

另一个好处是,当编译器知道类型时,如果参数与预期类型不匹配并且不能隐式转换为预期类型,它可以报错,如果return类型不匹配。