函数指针跨指针类型转换后:直接调用可靠吗?需转回原类型吗?
函数指针转换与调用的合法性问题
先看示例代码:
#include <stdio.h> typedef struct { int i; } A; void f(A* a) { printf("%d\n", a->i); } int main() { void (*pf)(void*) = (void(*)(void*))&f; A a; pf(&a); // 1 ((void(*)(A*))pf)(&a); // 2 }
问题
把类型为void(*)(A*)的函数指针强制转换成void(*)(void*)后,用A*类型的实参直接调用(标记1处)能否保证正确运行?还是必须像标记2处那样先转回原类型再调用?
答案
必须像标记2那样转回原函数指针类型再调用,才能保证跨平台的正确性与合规性;标记1的直接调用属于未定义行为,标准不保证其能正常运行。
详细说明
C标准的明确规则
C标准规定:函数指针可以在不同类型之间转换,也可以和void*转换(平台支持的前提下),但只有用与原函数类型兼容的指针调用时,行为才是确定的。原函数f的类型是void(*)(A*),只有用该类型的指针调用,才符合标准要求。标记1的潜在风险
在x86、x86_64这类常见平台上,A*和void*的内存大小、二进制表示完全一致,参数传递规则也相同,所以标记1的代码可能实际跑起来没问题。但这只是平台实现的巧合,不是标准强制要求:
- 有些特殊平台可能存在不同指针类型大小不同的情况(标准允许这种设计);
- 编译器会根据函数指针的声明类型做优化假设,标记1的写法可能触发编译器的异常优化,导致参数传递错误、栈帧损坏等不可预期的问题。
- 标记2的合法性
把pf转回原类型void(*)(A*)后再调用,完全符合标准:转换回原类型的函数指针,其值和原指针&f完全相等,调用效果和直接调用&f没有区别。
内容的提问来源于stack exchange,提问作者tmlen
相关产品推荐
相关产品推荐

