在 C23 之前如何对空指针使用“nullptr”?

Jan*_*tke 9 c c-preprocessor c23

在C23中,nullptr关键字得到了标准化。我也更愿意使用C23 之前nullptr的版本NULL,因为这意味着我可以编写可编译的代码:

  • C,C23之前
  • C,从 C23 开始
  • C++

我可以简单地NULL在两种语言和每个 C 标准中使用,但这在 C++ 代码中是非常不寻常的,并且nullptr无论如何正在成为两种语言的规范。

#define据我所知,语言中不允许使用替换关键字,这可能会在定义兼容性宏时引起问题。基本上,我需要:

// only if this is neither C++ nor C23
#define nullptr /* something */
Run Code Online (Sandbox Code Playgroud)

我怎样才能正确定义这个宏?

Lun*_*din 12

一些注意事项:

  • __STDC____STDC_VERSION__添加到“C95”(ISO C 9899:1990 的附录)中。
  • 您无法检查是否nullptr使用预处理器条件,因为它不是宏。
  • 即使使用符合标准的 C23 编译器,用户也可能会忘记包含stddef.h或等效标头,因此不要假设该标头是#included。
  • 当前版本的 gcc 和 clang 支持-std=c2x确实包含nullptr但设置__STDC_VERSION__为占位符值 202000L。
  • 并非所有 C++ 版本都包含该内容,nullptr因为它是在 C++11 中添加的。

因此,宏检查应该如下所示:

/* C++11 or later? */
#if (defined(__cplusplus) && __cplusplus >= 201103L)  
  #include <cstddef>

/* C2x/C23 or later? */
#elif ( defined(__STDC__) &&          \
        defined(__STDC_VERSION__) &&  \
        (__STDC_VERSION__ >= 202000L) )
  #include <stddef.h> /* nullptr_t */

/* pre C23, pre C++11 or non-standard */
#else
  #define nullptr (void*)0
  typedef void* nullptr_t;

#endif
Run Code Online (Sandbox Code Playgroud)

  • @JohnBollinger 好吧,你说服了我:)我更新了代码以使用 `(void*)0` 代替。 (2认同)
  • @JohnBollinger让我想知道如果将具有给定值的指针联合类型双关为“nullptr_t”,编译器应该如何表现。可能有很多 ISO WG 没有预见到的奇怪的极端情况:) (2认同)

Jan*_*tke 6

可以通过预定义宏检测您是否正在使用 C23 :

/* bad, don't do this */
#if !__cplusplus && __STDC_VERSION__ <= 201710
    #define nullptr ((void*)0)
#endif
Run Code Online (Sandbox Code Playgroud)

然而,这样一个简单的宏可能会触发编译器警告(clang -Wundef -std=c89):

<source>:1:6: warning: '__cplusplus' is not defined, evaluates to 0 [-Wundef]
    1 | #if !__cplusplus && __STDC_VERSION__ <= 201710
      |      ^
<source>:1:21: warning: '__STDC_VERSION__' is not defined, evaluates to 0 [-Wundef]
    1 | #if !__cplusplus && __STDC_VERSION__ <= 201710
      |  
Run Code Online (Sandbox Code Playgroud)

可以重写该解决方案,这样就不会触发警告:

/* don't do anything in C++, or if someone else defined a compatibility macro */
#if !defined(__cplusplus) && !defined(nullptr) && \
   (!defined(__STDC_VERSION__) || __STDC_VERSION__ <= 201710)
    /* -Wundef is avoided by using short circuiting in the condition */
    #define nullptr ((void*)0)
#endif
Run Code Online (Sandbox Code Playgroud)

请参阅编译器资源管理器中的实时示例

  • `#if !__cplusplus` 总是幼稚且危险的,应该是 `#if !define(__cplusplus)`。否则,未定义的宏的计算结果为 0,而 `!0` 的计算结果为 1。 (4认同)
  • 您还可以使用`#if !define(__STDC_VERSION__) || __STDC_VERSION__ &lt; 202311L` 也不会触发警告。 (2认同)