sag*_*ian 11 c pointers function-pointers
我有一个函数,它接受一个字符串,一个字符串数组和一个指针数组,并在字符串数组中查找字符串,并从指针数组中返回相应的指针.因为我将它用于几个不同的东西,指针数组被声明为(void*)的数组,并且调用者应该知道实际上有什么类型的指针(因此它返回的返回值是什么类型的指针) ).
但是,当我传入一个函数指针数组时,在编译时会收到警告-Wpedantic
:
铛:
test.c:40:8: warning: assigning to 'voidfunc' (aka 'void (*)(void)') from 'void *' converts
between void pointer and function pointer [-Wpedantic]
Run Code Online (Sandbox Code Playgroud)
GCC:
test.c:40:8: warning: ISO C forbids assignment between function pointer and ‘void *’ [-Wpedantic]
fptr = find_ptr("quux", name_list, (void **)ptr_list,
Run Code Online (Sandbox Code Playgroud)
这是一个测试文件,尽管警告确实正确打印"quux":
#include <stdio.h>
#include <string.h>
void foo(void)
{
puts("foo");
}
void bar(void)
{
puts("bar");
}
void quux(void)
{
puts("quux");
}
typedef void (* voidfunc)(void);
voidfunc ptr_list[] = {foo, bar, quux};
char *name_list[] = {"foo", "bar", "quux"};
void *find_ptr(char *name, char *names[], void *ptrs[], int length)
{
int i;
for (i = 0; i < length; i++) {
if (strcmp(name, names[i]) == 0) {
return ptrs[i];
}
}
return NULL;
}
int main() {
voidfunc fptr;
fptr = find_ptr("quux", name_list, (void **)ptr_list,
sizeof(ptr_list) / sizeof(ptr_list[0]));
fptr();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
有没有办法修复警告,除了不编译-Wpedantic
或复制我的find_ptr函数,一次用于函数指针,一次用于非函数指针?有没有更好的方法来实现我想要做的事情?
Art*_*Art 13
你无法修复警告.事实上,在我看来,它应该是一个很难的错误,因为将函数指针强制转换为其他指针是非法的,因为今天有一些架构,这不仅违反了C标准,而且是一个实际的错误,它将使代码不行.编译器允许它,因为许多架构都可以使用它,即使这些程序在某些其他架构上会严重崩溃.但它不仅仅是理论上的标准违规,它还会导致真正的错误.
例如在IA64函数指针(或至少曾经是我最后一次看)实际上是两个价值,既是必要的,使整个共享库或程序和共享库函数调用.同样,将函数指针转换为函数指针的常见做法是将函数返回给指向函数的指针返回void,因为你知道你将忽略返回值,这在ia64上也是非法的,因为这会导致陷阱值泄漏到寄存器中稍后会在一些不相关的代码段中导致崩溃.
不要强制转换函数指针.总是让他们匹配类型.这不仅仅是标准的迂腐,它是一个重要的最佳实践.
Ant*_*ala 10
语言律师的理由是“因为 C 标准没有明确允许它”。C11 6.3.2.3p1/p8
1.指向 void 的指针可以与指向任何对象类型的指针相互转换。指向任何对象类型的指针都可以转换为指向 void 的指针,然后再返回;结果应与原始指针相等。
8.指向一种类型函数的指针可以转换为指向另一种类型函数的指针,然后再返回;结果应与原始指针相等。如果使用转换后的指针调用类型与引用类型不兼容的函数,则行为未定义。
请注意,函数不是C 术语中的对象,因此无法将指向函数的指针转换为指向 void 的指针,因此行为未定义。
Castability tovoid *
是一个常见的扩展。C11 J.5 通用扩展 7:
J.5.7 函数指针强制转换
1.指向对象或指向 void 的指针可以转换为指向函数的指针,从而允许将数据作为函数调用 (6.5.4)。
2.指向函数的指针可以转换为指向对象或void 的指针,允许检查或修改函数(例如,由调试器)(6.5.4)。
这是例如 POSIX 所要求的 - POSIX 有一个dlsym
返回的函数,void *
但实际上它返回一个指向函数的指针或一个指向对象的指针,这取决于解析的符号的类型。
至于为什么会发生这种情况 - 如果实现可以达成一致,C 标准中没有任何内容是未定义或未指定的。然而,在某些平台上,假设 void 指针和函数指针具有相同的宽度,这确实会让事情变得困难。其中之一是 8086 16 位实模式。
那用什么来代替呢?您仍然可以将任何函数指针转换为另一个函数指针,因此您可以void (*)(void)
在任何地方使用通用函数指针。如果您同时需要函数指针void *
和函数指针,则必须使用结构体或联合体或分配void *
来指向函数指针,或确保您的代码仅在实现 J.5.7 的平台上运行;)
void (*)()
某些来源也推荐使用它,但现在它似乎在最新的 GCC 中触发警告,因为它没有原型。
这在C标准中早已被打破并且从未被修复过 - 没有通用指针类型可用于指向函数和指向数据的指针.
在C89标准之前,所有C编译器都允许在不同类型的指针之间进行转换,并且char *
通常用作指向任何数据类型或任何函数的通用指针.C89添加了void *
,但是在一个只有对象指针可以转换为的子句中void *
,没有定义对象是什么.POSIX标准通过强制要求void *
和函数指针可以来回安全地转换来解决这个问题.存在如此多的代码将函数指针转换为void *
并期望它正常工作.因此,几乎所有的C编译器仍然允许它,并且仍然生成正确的代码,因为任何编译器都不会被拒绝作为不可用的.
严格地说,如果你想在C中有一个泛型指针,你需要定义一个可以包含a void *
或a 的联合,void (*)()
并在调用它之前使用函数指针的显式强制转换为正确的函数指针类型.
一种解决方案是添加间接级别.这有助于解决很多问题.不存储指向函数的指针,而是存储指向存储指向函数的指针struct
的指针.
typedef struct
{
void (*ptr)(void);
} Func;
Func vf = { voidfunc };
ptrlist[123] = &vf;
Run Code Online (Sandbox Code Playgroud)
等等