【问题标题】:How can this structure have sizeof == 0?这个结构怎么会有 sizeof == 0?
【发布时间】:2018-05-01 08:23:09
【问题描述】:

有一篇旧帖子要求sizeof 将返回0 的构造。有一些来自高声誉用户的高分回答说,按照标准,没有类型或变量的大小可以为 0。我 100% 同意这一点。

但是有 this new answer 提供了这个解决方案:

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

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

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

哇……?

Godbolt link

这怎么可能?

【问题讨论】:

  • 零大小数组不是标准 C++。但扩展名。
  • @Jarod42 现在很明显了。
  • 在 MSVC 下也不能编译
  • 现在把它们放到一个数组中,对它们进行指针运算。
  • 嗯...如果不是 0,应该不占用多少字节?

标签: c++ language-lawyer sizeof


【解决方案1】:

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

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

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

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

标准的作者选择了#3。因此,标准“扩展”认为零大小的数组声明,即使在标准禁止此类构造之前得到广泛支持。

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

【讨论】:

  • “在 C 标准化之前”,您的意思是在 C99 存在之前?
  • @Stargateur:我的意思是从发明 C 到编写 C89 标准之间的大约 15 年。 C99 添加了一种有限形式的零大小对象,以避免一些必要的问题来解决其禁止零大小数组的问题,但如果 C89 没有令人讨厌地禁止零大小数组第一名。
  • “这些类型很有用”它们有什么用处?
  • @user109923:例如:struct polygon { int count; POINT sides[0];}; struct { struct polygon poly; POINT pts[3]; } myTriangle = { {3}, {{1,1},{2,2},{2,1} };。需要谨慎以确保对齐不会导致问题,但可能会有不同大小的静态持续时间对象可以互换处理。
【解决方案2】:

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

添加 -pedantic 会产生此警告:

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

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

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

【讨论】:

  • 无论哪种方式都令人惊讶,因为即使是空结构也应该产生非零的 sizeof 值...
  • 有趣的是,如果你创建了两个自动实例,它们被分开存储sizeof(int*)(我假设):coliru.stacked-crooked.com/a/e9a3038bf587b65c
  • 这只是回答零大小数组是非标准的,但它不能解释你问的问题。
  • 根据标准,行为不必以任何方式有意义 - 它只需要对编译器实现者有意义。编译器已经允许以标准禁止的方式定义类型。因此,标准中关于sizeof 为该类型给出的结果的要求也不适用。因此,该行为是由编译器开发人员做出的决定。
  • 思路是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) */ 当然,这不兼容标准! (例如,这可用于通过转换为结构来轻松解析动态长度的网络消息)。
【解决方案3】:

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

ISO/IEC 14882:2003 8.3.4/1:

[..] 如果 constant-expression (5.19) 存在,它应该是一个整数常量表达式并且它的值应该大于零。常量表达式指定数组的(元素数量)的边界。如果常量表达式的值为N,则数组有N元素编号为0N-1D的标识符类型为“derived-declarator-type-list 数组NT”。 [..]

g++ 需要 -pedantic 标志来对零大小的数组发出警告。

【讨论】:

    【解决方案4】:

    零长度数组是 GCC 和 Clang 的扩展。将sizeof 应用于零长度数组evaluates to zero

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

    【讨论】:

    • 所以...空类的大小不能为0,但非空类的大小可以为0...这没有多大意义。但我想这是你在使用非标准扩展时遇到的那种冲突的边缘情况。
    • @bolov; C++ 类(空)的大小不能为 0 符合空类的标准。 C++ 标准说,因为没有其他方法可以创建大小为0 的结构。在这种特殊情况下, struct it self 的成员大小为 0,这使得 struct 的大小为 0。我知道它令人困惑,但我尽力解释。
    • @bodov:但这不是 C++ 类,它是 G++ 类,因此 C++ 规则不必适用于它。特别注意,您不能拥有这种类型的数组或使用其指针进行数学运算,因此大小不能为零的通常推理也不成立。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-03-12
    • 2022-01-09
    • 2017-03-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多