更具体地说,如果我有以下函数指针类型:
typedef void (*callback_type) (intptr_t context, void* buffer, size_t count);
我可以安全地,没有“未定义的行为”吗:
callback_type func_ptr = (callback_type)write;
intptr_t context = fd;
func_ptr(context, some_buffer, buffer_size);
write()
是系统调用(编辑:具有签名ssize_t write(int fd, const void *buf, size_t count);
,因此int
作为第一个参数),fd
是{{1}文件描述符。我假设C和C ++的答案是相同的,所以我正在标记两者。
答案 0 :(得分:6)
这将不可移植,因为您传递的参数在公共LP64范例中将是不同的大小。
此外,您没有取消引用具有正确类型的函数指针,并且 的结果未定义。
现在,正如您似乎已经得出的结论,函数指针将按预期工作,唯一的实际问题是:如何写(2)解释intptr_t
第一个参数?
实际的运行时问题是,在LP64上,您将64位值传递给32位参数。这可能会使后续参数不对齐。在具有寄存器参数的系统上,它可能会正常工作。
答案 1 :(得分:2)
让我们来看看C标准。
C11(n1570),§6.3.2.3指针
指向一种类型的函数的指针可以转换为指向a的指针 另一种类型的功能又回来了;结果应该比较 等于原始指针。如果转换的指针用于调用 一个类型与引用类型不兼容的函数, 行为未定义。
C11(n1570),§6.7.6.3函数声明符(包括原型)
要兼容两种功能类型,两者都应指定兼容 返回类型。此外,参数类型列表,如果两者都存在, 应同意参数的数量和省略号的使用 终止;相应的参数应具有兼容的类型。
C11(n1570),§6.2.7兼容类型和复合类型
如果类型相同,则两种类型具有兼容类型。
结论:
void (*) (intptr_t context, void* buffer, size_t count);
无法转换为:
void (*) (int context, void* buffer, size_t count);
答案 2 :(得分:1)
问题不在于在函数之间来回传递参数,因为从一个整数类型到另一个整数类型的自动升级已经完成。
问题是,如果intptr_t
短于int
,那么int
的每个值都不能由intptr_t
表示?在这种情况下,转换为int
时,intptr_t
中的某些最高位将被截断,因此您将write()
结束无效的文件描述符。虽然这不应该调用未定义的行为,但它仍然是错误的。