这些部分表明,使用不兼容的类型调用函数指针会导致未定义的行为。
C89 3.5.4.3 p9
要使两种功能类型兼容,两者都应指定 兼容返回类型 。此外,参数类型列表(如果同时存在)应在参数数量和省略号终止符的使用上达成共识;相应的参数应具有兼容的类型。
C89 3.5.4.1 p2
要使两个指针类型兼容,则两者必须具有相同的资格,并且都应 指向兼容类型的指针 。
C89 3.3.4 p3
指向一种类型的函数的指针可以转换为指向另一种类型的函数的指针,然后再返回;结果应等于原始指针。 如果使用转换后的指针来调用类型与所调用函数的类型不兼容的函数,则行为是不确定的。
从其他任何对象指针类型转换为空指针是否兼容?
C89 3.3.4 p3
但是,可以保证可以将指向给定对齐对象的指针转换为指向相同对齐或不太严格对齐的对象的指针,然后再次返回;结果应等于原始指针。 (具有字符类型的对象的对齐方式最不严格。)
C89 3.1.2.5 p20
指向void的指针应与指向字符类型的指针具有相同的表示形式和对齐要求。
C89 3.2.2.3 p1
指向void的指针可以与任何不完整或对象类型的指针相互转换。 指向任何不完整或对象类型的指针可能会转换为指向void的指针并再次返回;结果应等于原始指针。
示例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int *foo(void) {
int *data = malloc(sizeof(int));
*data = 42;
return data;
}
int main(int argc, char *argv[]) {
void *(*fn_ptr)(void) = foo;
void *raw = fn_ptr();
int data = *(int *)raw;
printf("%d\n", data);
}
答案 0 :(得分:10)
您询问将指向一个函数的指针强制转换为另一个指向函数的指针是否合法。将指向函数的指针转换为指向函数的任何其他指针是合法的。但是通过这样的指针调用函数会调用未定义的行为。 C11 6.5.2.2p9
6.5.2.2函数调用
[...]
- 如果函数定义的类型与表示被调用函数的表达式所指向的类型(表达式的类型)不兼容,则行为是不确定的。
另一个问题是您的代码没有 cast 。它具有强制分配:
void *(*fn_ptr)(void) = foo;
这是无效的,具有约束冲突,C编译器必须诊断。演员会读
void *(*fn_ptr)(void) = (void *(*)(void))foo;
现在的问题是
的行为是什么void *(*fn_ptr)(void) = (void *(*)(void))foo;
void *raw = fn_ptr();
int data = *(int *)raw;
根据标准,未定义构造的行为。当然,您的实现可以自由地为表达式提供含义。在这种情况下,您应该查看编译器手册中的行为。
有些架构中void *
和int *
的表示形式
不会兼容。
如果仅将函数指针更改为返回int *
的函数指针,或者在调用之前进行强制转换,则行为是明确定义的-隐式转换为void *
并显式转换为int *
都很好。
即
int *(*fn_ptr)(void) = foo;
void *raw = fn_ptr();
int data = *(int *)raw;
或
void *(*fn_ptr)(void) = (void *(*)(void))foo;
void *raw = ((int *(*)(void))fn_ptr)();
int data = *(int *)raw;
或者让函数返回void *
:
void *foo(void) {
int *data = malloc(sizeof(int));
*data = 42;
return data;
}
void *(*fn_ptr)(void) = foo;
void *raw = fn_ptr();
int data = *(int *)raw;