我刚读到一个有趣的问题这里,它让我想起了两件事:
- 为什么有人要比较函数指针,因为按照设计,函数的唯一性是通过它们不同的名称来确保的呢?
- 编译器是否将函数指针视为特殊指针?我的意思是,它是否像指向
void *
的指针一样看待它们,或者它是否持有更丰富的信息(例如返回类型、参数数量和参数类型)?
我刚读到一个有趣的问题这里,它让我想起了两件事:
void *
的指针一样看待它们,或者它是否持有更丰富的信息(例如返回类型、参数数量和参数类型)?为什么有人需要比较函数指针?以下是其中一个例子:
#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));
register_function_for_event
函数的主体仅能看到arg
,无法看到任何函数名称。因此,它必须比较函数指针以报告某人注册了相同的函数两次。
如果您想支持类似unregister_function_for_event
的功能来补充上述内容,则唯一拥有的信息是函数地址。所以,您需要再次传递它,并进行比较,以允许删除。
至于更丰富的信息,当函数类型包含原型时,它是静态类型信息的一部分。请注意,在C中,函数指针可以声明为没有原型,但这是一种过时的特性。
为什么有人要比较指针?考虑以下场景 -
你有一个函数指针的数组,比如回调链,你需要调用它们中的每一个。该列表以一个NULL
指针(或标记值)终止。您需要通过与此标记指针进行比较来判断是否已到达列表的末尾。此外,这种情况证明了先前 OP 的担忧,即即使相似的函数也应具有不同的指针。
编译器是否会将它们视为不同?是的。类型信息包括有关参数和返回类型的所有信息。
例如,以下代码将/应被编译器拒绝 -
void foo(int a);
void (*bar)(long) = foo; // Without an explicit cast
void foo() {}
,那么 foo != NULL
。 - anon0
或 NULL
表示,而不是尝试获取实际函数的地址)。 - user2357112nullptr
进行比较时不会出现问题。只有在将两个实际函数相互比较时才会出现问题。 - Voo
- 为什么有人会比较函数指针,毕竟按照定义,函数的唯一性是通过它们不同的名称来确保的呢?
一个函数指针可以在程序执行过程中不同的时间指向不同的函数。
如果你有一个变量,例如:
void (*fptr)(int);
它可以指向任何接受int
为输入并返回void
的函数。
假设你有:
void function1(int)
{
}
void function2(int)
{
}
您可以使用:
fptr = function1;
foo(fptr);
或者:fptr = function2;
foo(fptr);
根据fptr
指向的函数不同,您可能希望在foo
中执行不同的操作。因此,需要:
if ( fptr == function1 )
{
// Do stuff 1
}
else
{
// Do stuff 2
}
- 编译器是否将函数指针视为特殊指针?我的意思是,它是否将它们视为指向void *的指针一样,还是它保存了更丰富的信息(例如返回类型、参数数量和参数类型)?
是的,函数指针是特殊指针,与指向对象的指针不同。
函数指针的类型在编译时拥有所有这些信息。因此,给定一个函数指针,编译器将拥有所有这些信息-返回类型、参数数量及其类型。
void*
更大:更具体地说,在PPC上,函数指针实际上是指针对的一组指针,其中一个指向代码,另一个指向全局引用表,该表取决于加载该函数的可执行文件/共享库。因此,在PPC上无法将函数指针强制转换为void*
,再转换回来。 - cmaster - reinstate monica关于函数指针的经典部分已经在其他答案中讨论过:
void *
,即使在C语言中也是如此)。但是C语言有一个(遗留的)函数声明模式。除了完整的原型模式声明返回类型和所有参数类型外,C还可以使用所谓的参数列表模式,这是旧的K&R模式。在此模式下,声明仅声明返回类型:
int (*fptr)();
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;
}
不要假装我建议你这样做!它现在被视为一种过时的功能,应该避免使用。我只是警告你不要使用它。在我看来,它只有极少数可接受的特殊用例。
add2
和add3
参数中的int
替换为long
,它会起作用吗?我的意思是:当你调用fptr(1,2)
时,它是否盲目地将1和2作为int发送,这在上面的示例中很方便,还是它是参数类型感知的? - Benjamin Barroisint (*)()
视为一种与int(*)(int, int)
不同的独特类型。因此,即使在C89标准中,我也不认为这是有效的标准C。当然,在实践中,指针只是地址,因此如果您确定底层调用约定,甚至可以在不同的函数指针类型之间进行疯狂的转换,并且在实践中它会起作用。 - Lundin1)有很多情况。以有限状态机的典型实现为例:
typedef void state_func_t (void);
const state_func_t* STATE_MACHINE[] =
{
state_init,
state_something,
state_something_else
};
...
for(;;)
{
STATE_MACHINE[state]();
}
if(STATE_MACHINE[state] == state_something)
{
print_debug_stuff();
}
2) 是的,C编译器将它们视为不同类型。实际上,函数指针比其他类型的C更具有严格的类型安全性,因为它们不能像对象类型的指针那样隐式转换为/从void*
(参见C11 6.3.2.3 / 1)。也不能显式地将其强制转换为/从void*
- 这样做会调用非标准扩展。
函数指针中的所有内容都很重要,以确定其类型:返回值的类型,参数的类型和参数的数量。所有这些都必须匹配,否则两个函数指针就不兼容。
switch
而不是函数指针列表。 - user541686WNDCLASS
类似的功能,该怎么做呢?lpszClassName
用于区分不同的窗口类,但是假设您不需要(或没有)可用于区分不同类之间的字符串。lpfnWndProc
(类型为 WindowProc
)。lpfnWndProc
两次调用 RegisterClass
,你会怎么做呢?WNDCLASS
的功能”而不是“WNDCLASS
”本身。老实说,我不确定C语言本身是否首先是现代抽象的典范... - user541686函数指针是变量。为什么要比较变量,毕竟按照概念,变量的唯一性由它们不同的名称保证?然而,有时两个变量可以具有相同的值,你想找出是否是这种情况。
C语言认为具有相同参数列表和返回值的函数指针属于相同类型。
int (*) (int)
。此类型存储函数应该如何调用的信息。 - user202729using ptr = int (*) (int); ptr a = f; ptr b = f; if (a == b) { /* a 等于 b */ }
- user202729