这个结构怎么会有 sizeof == 0?

How can this structure have sizeof == 0?

提问人:bolov 提问时间:11/17/2017 最后编辑:bolov 更新时间:11/22/2017 访问量:6814

问:

有一个旧帖子要求一个可以返回的构造.一些来自高声誉用户的高分回答说,根据标准,任何类型或变量都不能有 sizeof 0。我100%同意这一点。sizeof0

但是,有这个新答案提出了这个解决方案:

struct ZeroMemory {
    int *a[0];
};

我正要投反对票并发表评论,但在这里度过的时间教会了我检查我 100% 确定的事情。所以。。。令我惊讶的是,两者都显示了相同的结果:.更重要的是,变量的大小是:gccclangsizeof(ZeroMemory) == 00

ZeroMemory z{};
static_assert(sizeof(z) == 0); // Awkward...

Godbolt 链接

这怎么可能?

C++ 语言-律师 sizeof

评论

37赞 Jarod42 11/17/2017
零大小数组不是标准的 C++。但扩展。
0赞 bolov 11/17/2017
@Jarod42现在很明显了。
1赞 UnholySheep 11/17/2017
此外,它不会在 MSVC 下编译
7赞 CodesInChaos 11/18/2017
现在把它们放在一个数组中,对它们进行指针算术运算。
0赞 Gerhardh 11/18/2017
嗯......如果不是 0,则什么都不应该占用多少字节?

答:

43赞 bolov 11/17/2017 #1

正如 Jarod42 所指出的,零大小数组不是标准的 C++,而是 GCC 和 Clang 扩展。

添加会产生以下警告:-pedantic

5 : <source>:5:12: warning: zero size arrays are an extension [-Wzero-length-array]
    int *a[0];
           ^

我总是忘记(而不是)不会禁用所有扩展。std=c++XXstd=gnu++XX

这仍然不能解释这种行为。但至少我们知道这不是标准的......sizeof

评论

1赞 W.F. 11/17/2017
无论哪种方式,这都是令人惊讶的,因为即使是空结构也应该产生非零大小的值......
2赞 eerorika 11/17/2017
有趣的是,如果您创建两个自动实例,它们将被分开存储(我假设):coliru.stacked-crooked.com/a/e9a3038bf587b65csizeof(int*)
9赞 haccks 11/18/2017
这只能回答零大小数组是非标准的,但它并不能解释你提出的问题。
1赞 Peter 11/18/2017
根据标准,该行为不必以任何方式有意义 - 它只需要对编译器实现者有意义。编译器已经允许以标准禁止的方式定义类型。因此,标准中关于该类型给出的结果的要求也不适用。因此,该行为是由编译器开发人员做出的决定。sizeof
1赞 hoffmale 11/18/2017
这个想法是当然,这不是标准兼容的!(例如,这可用于通过强制转换为结构体来轻松解析动态长度的网络消息)。ZeroMemory* z = (ZeroMemory*)malloc(sizeof(ZeroMemory) + 2 * sizeof(int*)); z.a[1] = new int{1}; /* or whatever, just use indices into a to access dynamic memory after the previous members (in this case: none) */
19赞 msc 11/17/2017 #2

在 C++ 中,零大小数组是非法的。

ISO/IEC 14882:2003 8.3.4/1:

[..]如果常量表达式 (5.19) 存在,则它应该是一个整数常量表达式,其值应大于零。常量表达式指定数组的边界(元素数)。如果常量表达式的值为 ,则数组的元素编号为 ,标识符的类型为 “derived-declarator-type-list array of T”。[..]NN0N-1DN

G++ 要求该标志对零大小的数组发出警告。-pedantic

6赞 haccks 11/18/2017 #3

零长度数组是 GCC 和 Clang 的扩展。应用于长度为零的数组的计算结果为零sizeof

C++ 类(空)不能有 size ,但请注意该类不为空。它有一个具有大小的命名成员,应用将返回零。0ZeroMemory0sizeof

评论

5赞 bolov 11/18/2017
所以。。。空类不能有 size,但非空类可以有 size ...这没有多大意义。但我想这就是你在非标准扩展中得到的那种冲突的边缘情况。00
0赞 haccks 11/18/2017
@bolov;c++类(空)不能有大小0,这是空类的标准。 C++标准说,因为没有其他方法可以制作大小的结构。在这种特殊情况下,结构体的成员自身的大小为 0,这使得结构体的大小为 0。我知道这很令人困惑,但我尽力解释。0
3赞 Ben Voigt 11/18/2017
@bodov:但这不是一个C++类,它是一个 G++ 类,所以C++规则不必适用于它。特别要注意的是,你不能有这种类型的数组,也不能用它的指针做数学运算,所以通常认为大小不能为零的推理也站不住脚。
51赞 supercat 11/18/2017 #4

在 C 语言标准化之前,许多编译器在处理零大小类型时不会遇到困难,只要代码从未尝试从另一个指向零大小类型的指针中减去另一个指针。这种类型很有用,支持它们比禁止它们更容易、更便宜。然而,其他编译器决定禁止这种类型,一些静态断言代码可能依赖于这样一个事实,即如果代码试图创建一个零大小的数组,它们会发出嘶嘶声。该标准的作者面临着一个选择:

  1. 允许编译器以静默方式接受零大小的数组声明,甚至 如果此类声明的目的是触发 诊断和中止编译,并要求所有编译器都接受 这样的声明(尽管不一定是默默的)产生零 调整大小的对象。

  2. 允许编译器以静默方式接受零大小的数组声明,甚至 如果此类声明的目的是触发 诊断和中止编译,并允许编译器遇到这样的 声明要么中止编译,要么在闲暇时继续编译。

  3. 如果代码声明 零大小的数组,但随后允许实现中止 编译或继续它(使用他们认为合适的任何语义) 他们的闲暇。

该标准的作者选择了#3。因此,零大小数组声明被标准“扩展”视为,尽管在标准禁止它们之前,这种结构被广泛支持。

C++ 标准允许空对象的存在,但为了允许空对象的地址可用作令牌,它要求它们的最小大小为 1。因此,对于没有成员的对象,其大小为 0 将违反标准。但是,如果对象包含零大小的成员,则 C++ 标准对如何处理它没有要求,除了包含此类声明的程序必须触发诊断这一事实之外。由于使用此类声明的大多数代码都期望生成的对象的大小为零,因此对于接收此类代码的编译器来说,最有用的行为是以这种方式处理它们。

评论

0赞 Stargateur 11/22/2017
“在 C 被标准化之前”,你的意思是在 C99 存在之前?
1赞 supercat 11/22/2017
@Stargateur:我的意思是从C语言被发明到C89标准被写入之间的大约15年。C99 重新添加了一种有限形式的零大小对象,以避免一些绕过其禁止零大小数组所需的 kludge,但如果 C89 一开始没有令人讨厌地禁止零大小数组,那么这种 kludge 就没有必要了。
0赞 user109923 7/12/2019
“这种类型很有用”它们有什么用?
1赞 supercat 7/12/2019
@user109923:例如:.需要谨慎使用以确保对齐不会导致问题,但可以有不同大小的静态持续时间的对象可以互换处理。struct polygon { int count; POINT sides[0];}; struct { struct polygon poly; POINT pts[3]; } myTriangle = { {3}, {{1,1},{2,2},{2,1} };