nullptr_t 是否会破坏类型双关或指针转换?

Lun*_*din 10 c nullptr language-lawyer type-punning c23

考虑这个联盟:

\n
typedef union\n{\n  void*      vptr;\n  nullptr_t  nptr;\n} pun_intended;\n
Run Code Online (Sandbox Code Playgroud)\n

nullptr_t据说与void* 1)兼容。好吧,如果我们将 初始化void*为某个非零值怎么办?

\n
pun_intended foo = { .vptr = (void*)42 }; \n
Run Code Online (Sandbox Code Playgroud)\n
    \n
  • 根据 C23 6.3.2.3 \xc2\xa74,此转换被认为是合法的(impl.defined),或者至少在nullptr_t引入之前是合法的。
  • \n
  • 那么联合类型双关语又如何呢?据说也是合法的。
  • \n
  • 以及使用字符类型指针检查 C 中任何类型的内部表示,该指针在 C23, 6.3.2.3 \xc2\xa77 之前都是明确定义的。
  • \n
\n

完整示例:

\n
#include <stdio.h>\n#include <inttypes.h>\n#include <stddef.h>\n\ntypedef union\n{\n  void*      vptr;\n  nullptr_t  nptr;    \n} pun_intended;\n\nint main(void)\n{\n  pun_intended foo = { .vptr = (void*)42 };\n  printf("Value: %" PRIuPTR "\\n", (uintptr_t)foo.vptr);\n\n  if(foo.nptr != (void*)42)\n  {\n    puts("It does not have value 42.");\n    if(foo.nptr == nullptr)  \n      puts("Because it's a nullptr.");\n    else\n      puts("But it's not a nullptr.");\n\n    unsigned int val = *(unsigned char*)&foo; // little endian assumption here\n    printf("And it has value %d.\\n", val);\n\n    if(foo.vptr != nullptr)\n    {\n      puts("foo.vptr is however not a nullptr.");\n    }\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

clang 16 -std=c2x 上的输出:

\n
Value: 42\nIt does not have value 42\nBecause it's a nullptr\nAnd it has value 42.\nfoo.vptr is however not a nullptr\n
Run Code Online (Sandbox Code Playgroud)\n

gcc 13.2 -std=c2x 上的输出:

\n
Value: 42\nIt does not have value 42.\nBut it's not a nullptr.\nAnd it has value 42.\nfoo.vptr is however not a nullptr.\n
Run Code Online (Sandbox Code Playgroud)\n

我的问题:以上任何内容(以前是明确定义的或 impl.define 的)现在是未定义/未指定的行为吗?如果有的话,哪里有说明?或者 C23 中根本没有考虑这些场景 - 一个缺陷?

\n
\n

1)来源:C23 n3096 草案 7.21.2

\n
\n

的大小和对齐方式nullptr_t与指向字符类型的指针相同。该值的对象表示形式nullptr与 类型的空指针值的对象表示形式相同void*

\n
\n

Eri*_*hil 8

\n

好吧,如果我们将 初始化void*为某个非零值怎么办?

\n
\n

C 2023 N3096 7.21.2 3 明确回答了这个问题。nullptr在告诉我们类型中值的表示nullptr_t与类型中空指针值的表示相同之后void *,它告诉我们如果对象中存在不同的字节值序列会发生什么nullptr_t

\n
\n

\xe2\x80\xa6 如果对象表示不同,则行为未定义。

\n
\n

  • 啊。这正是 C 所需要的:语法糖,为 C 添加了全新的不可变类型概念,并为奇怪的极端情况 UB 和错误打开了大门。直接来自过于复杂、难以理解、必须对所有人都适用的疯狂字体 C++。 (2认同)