【问题标题】:Can I reliably set a function pointer to NULL in C and C++?我可以在 C 和 C++ 中可靠地将函数指针设置为 NULL 吗?
【发布时间】:2016-02-06 20:47:19
【问题描述】:

在 P.J. Plauger 的书标准 C 库中,他警告不要将函数指针分配给 NULL。

具体来说,他是这样说的:

NULL 用作几乎通用的空指针常量。您将其用作数据对象指针的值,该指针应指向程序中未声明(或分配)的数据对象。正如我在第 216 页提到的,宏可以具有以下任何定义 0、0L 或 (void *)0。

最后一个定义与任何数据对象指针兼容。但是,它与函数指针兼容。这意味着你不能写

int (*pfun) (void) = NULL; /* WRONG */

翻译器可能会抱怨表达式类型与您希望初始化的数据对象不兼容。

他接着说:

...但是,不能保证指向 void 的指针与任何其他(非字符)指针具有相同的 表示。它甚至与函数指针的赋值都不兼容。 这意味着您不能将 NULL 写为通用空指针常量。您也不能安全地将其用作参数表达式来代替任意数据对象指针。

我已经将函数指针分配给NULL 很长一段时间没有任何问题,我想知道它是否不可移植。

具体来说:

void (*test)() = NULL => 用 gcc 和 g++ 都可以正常编译

void (*test)() = 0 => 用 gcc 和 g++ 都可以正常编译

void (*test)() = (void*)0 => 在 gcc 和 g++ 中都产生了无效的转换错误

编辑:void (*test)() = (void*)0在 gcc 中编译良好,我使用的是扩展名为 .cpp 的文件... 尽管如此,尽管 Plauger 说将函数指针分配给 NULL 是错误的,但它会一直编译吗?

我不明白的部分是我的stddef.h中NULL的定义:

#if defined (_STDDEF_H) || defined (__need_NULL)
#undef NULL     /* in case <stdio.h> has defined it. */
#ifdef __GNUG__
#define NULL __null
#else   /* G++ */
#ifndef __cplusplus
#define NULL ((void *)0) // this line confuses me
#else   /* C++ */
#define NULL 0
#endif  /* C++ */
#endif  /* G++ */
#endif  /* NULL not defined and <stddef.h> or need NULL.  */
#undef  __need_NULL

这似乎将NULL 在 C++ 中定义为 0,在 C 中定义为 ((void *)0)。是真的,还是被定义为 __null?

如果是这样,为什么分配给 NULL 总是有效,即使根据 Plauger 的说法,分配给 (void*)0 是“错误的”?**

我对 C89 感兴趣**

【问题讨论】:

  • __null 定义仅在 G++ 中。
  • 我就是这么想的,谢谢。
  • 在 C 中 NULL((void *)0) 和在 C++ 中 0 是正常的。哪一部分你不明白?
  • @M.M Plauger 说将 NULL 分配给函数指针的部分是错误的,特别是当它被定义为 (void*)0 时。
  • 一些旧的编译器,如 solaris 上的编译器,实际上确实会为这种类型的赋值发出警告,如 assignment mismatch: pointer to function(...) returning void "=" pointer to void

标签: c++ c pointers gcc g++


【解决方案1】:
 int (*pfun) (void) = NULL;  

确实有效。

C 的赋值规则是这样说的:

(请注意,这里是初始化,但适用与简单赋值相同类型的约束和转换。)

(C99, 6.5.16.1 Simple assignment p1 Constraints) “以下之一应成立:[...] — 左侧操作数是指针,右侧是空指针常量;”

(C99, 7.17p3) "宏为 NULL,扩展为实现定义的空指针常量;"

因此,C 允许将空指针常量分配给任何指针(对象指针、函数指针或void *)。请注意,Plauger 的书在提到标准 C 时指的是 C89,但分配约束的措辞是相同的在 C89 中。

【讨论】:

  • 这是一个很好的观点,但对于 C89 是否也一样?对不起,我没有在我的问题中说得很明显。 (忽略这个问题)
  • @rationalcoder 在我的最后一句话中我明确地说它在 C89 中是一样的。
  • 哇,抱歉。如果相同,那么他说它是错误的是错误的吗?它总是适用于符合 C89 的编译器?
  • @rationalcoder 对我说,pfun 声明 错误 是不正确的,恕我直言,声明是有效的。
  • @rationalcoder 顺便说一句,c-faq 似乎同意 c-faq.com/null/fcnptr.html
【解决方案2】:

void (*test)() = (void*)0 => 在 gcc 和 g++ 中都产生了无效的转换错误

GCC 根据文件扩展名检测语言。使用 GCC 编译 .cc 文件将调用 C++ 编译器,而不是 C 编译器。

用 C 源文件尝试一下,你会发现它被接受了。它应该是,因为它是标准允许的。

如果是这样,为什么分配给 NULL 一直有效,但分配给 (void*)0 却不行?

NULL 不允许在 C++ 模式下定义为 ((void*)0)。在 C++ 模式下,它必须定义为值为 0 的整型常量或 nullptr。两者都可以转换为任何函数指针类型。

我可以在 C 和 C++ 中始终可靠地将函数指针设置为 NULL 吗?

是的,在任何符合标准的 C 或 C++ 实现中都可以使用。

【讨论】:

  • 你有GCC detects the language based on the file extension. Compiling a .cc file with GCC will invoke the C++ compiler, not the C compiler.的参考吗?
  • 哈!我没想到我没有使用不同的文件,谢谢!
  • @mch GCC manual: Compiling C++ Programs:“GCC 识别具有这些名称 [.cc 和前面提到的其他名称] 的文件并将它们编译为 C++ 程序,即使您调用编译器的方式与编译 C 程序的方式相同(通常名称为gcc)。"
  • 那么,当 (void*) 和 void(*)() 不能直接赋值时,为什么它会起作用?
  • @rationalcoder:看起来他犯了一个错误。它发生了。
猜你喜欢
  • 1970-01-01
  • 2011-10-14
  • 1970-01-01
  • 2013-10-31
  • 1970-01-01
  • 2010-09-20
  • 2021-02-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多