6

更具体地说,如果我有以下函数指针类型:

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);,因此将 aint作为第一个参数),并且fdint文件描述符。我假设 C 和 C++ 的答案是相同的,所以我标记了两者。

4

3 回答 3

6

这不会是可移植的,因为您传递的参数在常见的 LP64 范例中将具有不同的大小。

此外,您没有取消引用具有正确类型的函数指针,其结果未定义的。

现在,正如您似乎已经得出的结论,函数指针将按预期工作,唯一的实际问题将是:write(2) 将如何解释第intptr_t一个参数?

实际的运行时问题是,在 LP64 上,您将 64 位值传递给 32 位参数。这可能会使后续参数错位。在具有寄存器参数的系统上,它可能工作得很好。

于 2013-04-06T16:46:09.180 回答
2

让我们看一下C标准。

C11 (n1570),第 6.3.2.3 节指针

指向一种类型的函数的指针可以转换为指向另一种类型的函数的指针,然后再返回;结果应与原始指针比较。如果转换后的指针用于调用类型与引用类型不兼容的函数,则行为未定义。

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);
于 2013-04-06T16:57:14.100 回答
1

问题不在于在函数之间来回传递参数,因为已经完成了从一种整数类型到另一种的自动提升。

问题是,如果intptr_t比 短怎么办int,因此不是每个值int都可以用一个来表示intptr_t?在这种情况下,在转换为 时, 中的一些最高位int将被截断intptr_t,因此您最终会write()得到一个无效的文件描述符。尽管这不应该调用未定义的行为,但它仍然是错误的。

于 2013-04-06T16:42:46.240 回答