【问题标题】:Why does a function pointer declaration need to know the types of the parameters and return value?为什么函数指针声明需要知道参数和返回值的类型?
【发布时间】:2019-07-30 00:59:22
【问题描述】:

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

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 是否仅用于支持编译器的错误检查? (例如,在用户分配类型不匹配的函数时提醒用户)。

【问题讨论】:

  • 什么是类型系统,如果不是仅用于支持编译器错误检查的额外信息
  • 函数原型存在的原因完全一样

标签: c pointers compiler-construction function-pointers static-typing


【解决方案1】:

有几个原因。我们先考虑返回值。

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

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

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

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

【讨论】:

    【解决方案2】:

    是的,您询问的“额外信息”主要是“为了支持编译器进行错误检查”。

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

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

    #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 是一个指向函数的指针,该函数采用未指定的参数,并返回int。 (“函数采用未指定的参数”是在 ANSI C 之前,在发明函数原型之前,所有函数和函数指针的描述方式。)如图所示,您可以使用 allegedly_generic_function_pointer 指向 - 并调用 - 采用不同的函数论据。 (不过,您的编译器可能会给您各种“有用的”警告。)

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

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

    如果你想要一个指向带有未指定参数的函数的指针 具有未指定的返回类型,这是你在 C 中根本无法做到的事情之一。(你为什么想要这样一个事情?问我曾经写过的 C 解释器。)

    【讨论】:

      猜你喜欢
      • 2012-04-05
      • 1970-01-01
      • 2013-03-16
      • 1970-01-01
      • 2019-11-20
      • 2018-11-11
      • 1970-01-01
      • 1970-01-01
      • 2011-07-17
      相关资源
      最近更新 更多