【问题标题】:struct hack - zero sized arraystruct hack - 零大小的数组
【发布时间】:2015-10-20 21:17:39
【问题描述】:
#include <iostream>
using namespace std;

struct node1{
    char b[3];
    int c[0];
};

struct node2{
    int c[0];
};

struct node3{
    char b[3];
};


int main() {

    cout << sizeof(node1) << endl;  // prints 4
    cout << sizeof(node2) << endl;  // prints 0
    cout << sizeof(node3) << endl;  // prints 3
}

我的问题是为什么编译器在 node2 中为 int c[0] 分配 0 个字节 但在 node1 的一部分时为其分配 1 个字节。 我假设这 1 个字节是 sizeof(node1) 返回 4 的原因,因为没有它(如在 node3 中)它的大小为 3 还是由于填充??

还试图理解 node2 不应该有足够的空间来保存指向数组的指针(将在代码的更下方作为灵活数组/结构黑客的一部分分配?

【问题讨论】:

  • @BaummitAugen:嗯,我觉得很傻。可以复制:coliru.stacked-crooked.com/a/f1a26629a75b8d01
  • 假设:node1 具有填充以确保 node1node1.c 在 4 字节边界上对齐,因为 int 具有 4 字节对齐要求。大小为 0 的数组没有特殊情况,因此不会忽略 int c[0]
  • 由于零长度数组在 C++ 中不是一个东西,请告诉我们你在说什么 C++ 方言。
  • 你用的是什么编译器? MSVC 为sizeof(node2) 给出 1,但警告说 0 大小的数组是非标准的。所以答案是你正在调用非指定的行为所以结果取决于实现!
  • @SergeBallesta 编译器是 Apple LLVM 7.0。在 XCode 中打开迂腐警告后,我看到警告为“零大小数组是扩展”

标签: c++ struct g++ sizeof


【解决方案1】:

是的,这是关于填充/对齐的。如果您将__attribute__((__packed__)) 添加到末尾[在编写设备驱动程序时有用],您将获得3 0 3 作为您的输出。

如果 node1 已定义 c[1],则大小为 8 而不是 7,因为编译器会将 c 对齐到 int 边界。打包后,sizeof 为 7

【讨论】:

    【解决方案2】:

    是的,填充会有所不同。 node1 有填充字节而node3 没有填充字节的原因在于零长度数组的典型用法。

    零长度数组通常与转换一起使用:您将一个更大的(可能是可变大小的)对象转换为包含零长度数组的结构。然后,您使用零长度数组访问大对象的“其余部分”,为此,必须正确对齐。填充字节插入到大小为零的数组之前,以使ints 对齐。由于node3 无法做到这一点,因此不需要填充。

    例子:

    struct Message {
       char Type[3];
       int Data[];    // it compiles without putting 0 explicitly
    };
    
    void ReceiveMessage(unsigned char* buffer, size_t length) {
        if(length < sizeof(Message))
            return;
        Message* msg = (Message*)buffer;
        if(!memcmp(msg->Type, "GET", 3)) {
            HandleGet(msg->Data, (length - sizeof(Message))/sizeof(int));
        } else if....
    

    注意:这很老套,但很有效。

    【讨论】:

      【解决方案3】:

      c 不会在node1 中分配一个字节。这是因为在b 中添加了填充。

      对于b,为了便于 32 位 CPU 获取,它有四个字节大。 32 位 CPU 一次可以从内存中读取 4 个连续字节。要阅读三,他们必须阅读四,然后删除不必要的。因此,为了优化这种行为,编译器用一些字节填充struct

      当值被压入堆栈(即分配参数或局部变量)时,您可以观察到类似的编译器优化。堆栈始终保持与 CPU 的数据总线大小(通常为 32 或 64 位)对齐。

      【讨论】:

      • 嗯,它分配一个字节,但这是因为b需要填充。
      • 这个答案似乎暗示node3也应该有填充,但它没有。
      • @MooingDuck 我认为“It”指的是c(不记得了:-))。将清除它。
      • @JohnKugelman 没错,这就是将 node3 添加到这个程序的目的
      • @MatsPetersson 是的,我明白这一点,但这不是答案所说的。答案是b 是四个字节,很容易获得。这不是原因:都是关于c
      【解决方案4】:
      int main() {
      
        cout << sizeof(node1) << endl;  // prints 4
        cout << sizeof(node2) << endl;  // prints 0
        cout << sizeof(node3) << endl;  // prints 3
      }
      

      主函数查询用户定义结构的大小,而不是数组成员的大小。 sizeof() 将返回分配给结构的字节数,字符数组中分配的每个字符分配 1 个字节。字符数组实际上是一个 C 风格的字符串,它以标记字符 '\0' 终止。在评估 sizeof(node1) 时可能包括分配用于保存标记字符的字节,因为它后面还有另一个变量,因此它会读取它,但不包括字符串和结构终止的 sizeof(node3) 中的标记

      【讨论】:

      • 前哨字符与此行为无关。
      猜你喜欢
      • 2020-10-16
      • 1970-01-01
      • 2018-02-27
      • 1970-01-01
      • 1970-01-01
      • 2021-12-22
      • 2023-04-04
      • 1970-01-01
      • 2015-07-28
      相关资源
      最近更新 更多