struct hack - 零大小的数组

时间:2015-10-20 21:17:39

标签: c++ struct g++ sizeof

#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是否应该有足够的空间来保存指向数组的指针(作为灵活数组/结构黑客攻击的一部分,它将在代码中进一步分配?

4 个答案:

答案 0 :(得分:2)

是的,它是关于填充/对齐的。如果您将__attribute__((__packed__))添加到最后[在编写设备驱动程序时很有用],那么您的输出将获得3 0 3

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

答案 1 :(得分:2)

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

零长度数组通常与强制转换一起使用:将较大的(可能是可变大小的)对象强制转换为包含零长度数组的结构。然后使用零长度数组访问大对象的“其余”,为此,必须正确对齐。在零大小的数组之前插入填充字节,以使int对齐。由于您无法使用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....

注意:这是相当hackish,但效率很高。

答案 2 :(得分:0)

c并未在node1中分配一个字节。这是因为填充添加到b

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

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

答案 3 :(得分:0)

int main() {

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

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