我试图找出以下前向声明在ANSI-C中是否有效:
第一档:
Close()
第二档:
extern void * fptr; // opaque forward declaration.
int main (void) {
fptr = NULL; // set the function pointer to NULL
}
对我来说,如果typedef int (*fptr_t)(int);
fptr_t fptr; // real declaration of the function pointer
声明为两种不同的类型,那么这应该是无效的,但fptr
和gcc
都不会发出任何警告。
我会更加特别关注C11标准的精确要点,以便得出它有效(或无效)的原因。
编辑:在C11标准中,6.2.7:2说:
引用同一对象或函数的所有声明都应具有 兼容型;否则,行为未定义。
但我无法确定如何确定clang
是否与void*
兼容。
答案 0 :(得分:7)
C99:
6.2.7兼容类型和复合类型
第2条:
引用同一对象或函数的所有声明都应具有兼容类型;否则,行为未定义。
6.7.5.1指针声明符
第2条:
要使两个指针类型兼容,两者都应具有相同的限定条件,并且两者都应是兼容类型的指针。
如果不进一步深入挖掘标准,很容易看出void
和函数不兼容类型。
我愿意打赌这在C11中没有变化。 C已经隐式支持不同的代码和数据空间以及代码和数据指针的不同大小和表示很长一段时间,删除此功能并将语言限制在可用的较小机器子集中会很奇怪。因此,谨慎投票。更好的证明。
答案 1 :(得分:6)
不,它无效,因为基本上您将常规指针(NULL
,void*
)存储到实际上是函数指针的内存位置。您只是将其从编译器中隐藏起来,并且链接器并不关心,但是在一天结束时您有未定义的行为,因为两个指针类型不一定兼容。当然,它可能适用于许多系统,但也许并非全部。
有关函数指针与void指针的更多信息,请参见此处:can void* be used to store function pointers? - 虽然这与您所呈现的情况略有不同,但答案仍然相关。
答案 2 :(得分:1)
应该有效,但无效。在第一个文件中,您声明标识符fptr
将在另一个编译单元中定义,并且它将是void *
。在第二个文件中,您定义标识符,但它现在是一个指向函数的指针。编译后的文件通常不保留对象的类型(只有地址),所以:
在通常的实现中,所有指针都具有相同的表示形式,将指向函数的指针转换为指向void的指针不会改变表示形式,因此别名将给出预期的结果。
但是每个标准仍然是未定义的行为(*),因为6.2.5类型§27声明(强调我的):
指向void的指针应具有与a相同的表示和对齐要求 指向字符类型的指针.39)同样,指向合格或非限定版本的指针 兼容类型应具有相同的表示和对齐要求。所有指向结构类型的指针都应具有相同的表示和对齐要求 彼此相同。所有指向联合类型的指针都应具有相同的表示形式 对齐要求彼此。 指向其他类型的指针不必相同 表示或对齐要求。
(*)由于没有通过常规实现进行测试,因此当前版本的编译器无法检测和优化UB。但我永远不会在生产代码中这样做......
答案 3 :(得分:1)
void *
是一个对象类型指针,它与函数类型指针不同。它们不兼容。
可是:
可行性问题 J.5.7 。函数指针强制转换
指向对象或void的指针可能 被强制转换为指向函数的指针,允许将数据作为一个调用 功能(6.5.4)。
- 醇>
可以将指向函数的指针强制转换为指针 对象或无效,允许检查功能或 修改(例如,通过调试器)(6.5.4)。
那么为什么不完全隐藏模块中的指针,并外化函数来操作它?这样可以避免混叠问题。