C中的函数指针 - 性质和用法

Ben*_*ois 32 c pointers compilation

我刚刚在这里阅读了一个有趣的问题,让我想到了另外两件事:

  1. 为什么有人应该比较函数指针,因为通过概念,函数的唯一性由它们的不同名称来保证?
  2. 编译器是否将函数指针视为特殊指针?我的意思是它看到他们喜欢,让我们说,指针void *或是否持有更丰富的信息(如返回类型,论点和论据类型的号码?)

Sto*_*ica 36

为什么要比较函数指针?这是一个例子:

#include <stdbool.h>

/*
 * Register a function to be executed on event. A function may only be registered once.
 * Input:
 *   arg - function pointer
 * Returns:
 *   true on successful registration, false if the function is already registered.
 */
bool register_function_for_event(void (*arg)(void));

/*
 * Un-register a function previously registered for execution on event.
 * Input:
 *   arg - function pointer
 * Returns:
 *   true on successful un-registration, false if the function was not registered.
 */
bool unregister_function_for_event(void (*arg)(void));
Run Code Online (Sandbox Code Playgroud)

身体register_function_for_event只能看到arg.它没有看到任何函数名称.它必须比较函数指针以报告某人两次注册相同的函数.

如果你想支持unregister_function_for_event上述内容,你唯一的信息就是函数地址.因此,您需要再次传递它,并与之进行比较,以允许删除.

至于更丰富的信息,是的.当函数类型包含原型时,它是静态类型信息的一部分.请注意,在C中,可以在没有原型的情况下声明函数指针,但这是一个过时的功能.


Aja*_*iya 19

  1. 为什么有人会比较指针?请考虑以下情况 -

    你有一个函数指针数组,比如它是一个回调链,你需要调用它们中的每一个.该列表以NULL(或标记)函数指针终止.您需要通过与此哨兵指针进行比较来比较是否已到达列表的末尾.此外,这种情况证明以前的OP担心不同的函数应该有不同的指针,即使它们是相似的.

  2. 编译器是否以不同方式看待它们 是.类型信息包括有关参数和返回类型的所有信息.

    例如,编译器将拒绝以下代码 -

    void foo(int a);
    void (*bar)(long) = foo; // Without an explicit cast
    
    Run Code Online (Sandbox Code Playgroud)

  • @NicHartley:答案在终止一系列函数指针的上下文中提出了主张.在该上下文中,空函数指针将是自然选择(并且这当然将表示为"0"或"NULL"而不是尝试获取实际函数的地址). (3认同)

R S*_*ahu 18

  1. 为什么有人应该比较函数指针,因为通过概念,函数的唯一性由它们的不同名称来保证?

函数指针可以指向程序中不同时间的不同函数.

如果你有一个变量,如

void (*fptr)(int);
Run Code Online (Sandbox Code Playgroud)

它可以指向任何接受int输入并返回的函数void.

假设你有:

void function1(int)
{
}

void function2(int)
{
}
Run Code Online (Sandbox Code Playgroud)

您可以使用:

fptr = function1;
foo(fptr);
Run Code Online (Sandbox Code Playgroud)

要么:

fptr = function2;
foo(fptr);
Run Code Online (Sandbox Code Playgroud)

您可能希望foo根据是fptr指向一个函数还是另一个函数来执行不同的操作.因此,需要:

if ( fptr == function1 )
{
    // Do stuff 1
}
else
{
    // Do stuff 2
}
Run Code Online (Sandbox Code Playgroud)
  1. 编译器是否将函数指针视为特殊指针?我的意思是它会看到它们,比方说,指向void*或者它是否包含更丰富的信息(如返回类型,参数数量和参数类型?)

是的,函数指针是特殊指针,不同于指向对象的指针.

函数指针的类型在编译时具有所有信息.因此,给出一个函数指针,编译器将拥有所有那些信息 - 返回类型,参数数量及其类型.

  • 我的第二个是@PlasmaHH所说的,那里有CPU架构,其中函数指针大于`void*`:更具体地说,PPC上的函数指针实际上是指针对,一个指向代码,另一个指向点到全局引用表,该表依赖于从中加载函数的可执行文件/共享库.因此,不可能将函数指针转换为"void*"并返回PPC. (3认同)

Ser*_*sta 5

关于函数指针的经典部分已经在其他答案中讨论过:

  • 像其他指针一样,指向函数的指针可以在不同的时间指向不同的对象,因此比较它们很有意义。
  • 函数指针是特殊的,不应存储在其他指针类型中(偶数void *也不是C语言)。
  • 丰富部分(功能签名)存储在功能类型中-产生上述语句的原因。

但是C具有(旧式)函数声明模式。除了声明返回类型和所有参数的类型的完整原型模式外,C还可以使用所谓的参数列表模式,即旧的K&R模式。在这种模式下,声明仅声明返回类型:

int (*fptr)();
Run Code Online (Sandbox Code Playgroud)

在C语言中,它声明一个指向函数的指针,该函数返回an int接受任意参数。只是将它与错误的参数列表一起使用将是未定义的行为(UB)。

所以这是合法的C代码:

#include <stdio.h>
#include <string.h>

int add2(int a, int b) {
    return a + b;
}
int add3(int a, int b, int c) {
    return a + b + c;
}

int(*fptr)();
int main() {
    fptr = add2;
    printf("%d\n", fptr(1, 2));
    fptr = add3;
    printf("%d\n", fptr(1, 2, 3));
    /* fprintf("%d\n", fptr(1, 2)); Would be UB */
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

不要假装我已建议您这样做!现在,它被视为过时的功能,应避免使用。我只是警告你反对。恕我直言,它只能有一些特殊的可接受的用例。

  • @Lundin-[6.3.2.3](http://port70.net/~nsz/c/c11/n1570.html#6.3.2.3)表示转换有效。并且[6.7.6.3p15](http://port70.net/~nsz/c/c11/n1570.html#6.7.6.3p15)表示指针类型是虚空兼容的(因为没有参数类型列表) )。因此,这都是有效且危险的C。 (2认同)