繁体   English   中英

这个函数指针的前向声明在C中是否有效?

[英]Is this forward declaration of a function pointer valid in C?

我试图找出以下前向声明在ANSI-C中是否有效:

第一档:

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如果用两种不同的类型声明,但gccclang都没有给出任何警告。

我会更具体地对C11标准的精确点感兴趣,这些点可以得出结论为什么它有效(或无效)。


编辑:在C11标准中,6.2.7:2说:

引用同一对象或函数的所有声明都应具有兼容类型; 否则,行为未定义。

但是我找不到如何判断void*是否与fptr_t兼容。

C99:

6.2.7兼容型和复合型

第2条:

引用同一对象或函数的所有声明都应具有兼容类型; 否则,行为未定义。

6.7.5.1指针声明符

第2条:

要使两个指针类型兼容,两者都应具有相同的限定条件,并且两者都应是兼容类型的指针。

如果不进一步深入挖掘标准,很容易看出void和功能不兼容。

我愿意打赌这不会改变C11。 C已经隐式支持不同的代码和数据空间以及代码和数据指针的不同大小和表示很长一段时间,删除此功能并将语言限制在可用的较小机器子集中会很奇怪。 因此,谨慎投票。 更好的证据。

不,它无效,因为基本上你将一个常规指针( NULLvoid* )存储到一个实际上是函数指针的内存位置。 您只是将其从编译器中隐藏起来,并且链接器并不关心,但是在一天结束时您有未定义的行为,因为两个指针类型不一定兼容。 当然它可能适用于许多系统,但也许并非全部。

有关函数指针vs void指针的更多信息,请参见: can void *用于存储函数指针? - 虽然这与你所呈现的情况略有不同,但答案仍然相关。

它应该工作,但它是无效的。 在第一个文件中,您声明标识符fptr将在另一个编译单元中定义,并且它将是void * 在第二个文件中,您定义标识符,但它现在是一个指向函数的指针。 编译后的文件通常不保留对象的类型(只有地址),所以:

  • 编译器不知道其他源声明了不同的类型,并且不能发出任何警告
  • 链接器不需要控制它,因此标准不需要警告,常见的实现不控制类型,因此也没有警告

在通常的实现中,所有指针具有相同的表示,并且指向函数的指针到指向void的指针的转换不会改变表示,因此别名将给出预期的结果。

但它仍然是每个标准的未定义行为 (*),因为6.2.5类型§27声明(强调我的):

指向void的指针应具有与指向字符类型的指针相同的表示和对齐要求.39)类似地,指向兼容类型的限定或非限定版本的指针应具有相同的表示和对齐要求。 所有指向结构类型的指针都应具有相同的表示和对齐要求。 所有指向union类型的指针都应具有相同的表示和对齐要求。 指向其他类型的指针不需要具有相同的表示或对齐要求

(*)由于没有通过常规实现进行测试,因此当前版本的编译器将检测和优化UB的风险很小。 但我永远不会在生产代码中这样做......

void *是一个对象类型指针,它与函数类型指针不同。 它们不兼容。

但:

可行性问题J.5.7 函数指针强制转换

  1. 指向对象或void的指针可以转换为指向函数的指针,允许将数据作为函数调用(6.5.4)。

  2. 指向函数的指针可以转换为指向对象的指针或void,允许检查或修改函数(例如,通过调试器)(6.5.4)。

那么为什么不完全隐藏模块中的指针,并外化函数来操纵它? 这样可以避免混叠问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM