C struct hack at work

这是如何使用C struct hack时分配的“额外”内存?

问题:

我在下面有一个C struct hack实现。 我的问题是如何使用我分配给hack的“额外”内存。 有人可以给我一个使用额外内存的例子吗?

#include #include int main() { struct mystruct { int len; char chararray[1]; }; struct mystruct *ptr = malloc(sizeof(struct mystruct) + 10 - 1); ptr->len=10; ptr->chararray[0] = 'a'; ptr->chararray[1] = 'b'; ptr->chararray[2] = 'c'; ptr->chararray[3] = 'd'; ptr->chararray[4] = 'e'; ptr->chararray[5] = 'f'; ptr->chararray[6] = 'g'; ptr->chararray[7] = 'h'; ptr->chararray[8] = 'i'; ptr->chararray[9] = 'j'; } 

是的,这是(并且是) C用于创建和处理可变大小struct的标准方法。

这个例子有点冗长。 大多数程序员会更灵巧地处理它:

 struct mystruct { int len; char chararray[1]; // some compilers would allow [0] here }; char *msg = "abcdefghi"; int n = strlen (msg); struct mystruct *ptr = malloc(sizeof(struct mystruct) + n + 1); ptr->len = n; strcpy (ptr->chararray, msg); } 

自从我读到这篇文章( http://blogs.msdn.com/b/oldnewthing/archive/2004/08/26/220873.aspx )以来,我一直喜欢使用struct hack:

  #include #include int main() { struct mystruct { int len; char chararray[1]; }; int number_of_elements = 10; struct mystruct *ptr = malloc(offsetof(struct mystruct, chararray[number_of_elements])); ptr->len = number_of_elements; for (i = 0; i < number_of_elements; ++i) { ptr->chararray[i] = 'a' + i; } } 

我发现不必记住1是否需要减去(或添加或其他)是好的。 这也有在数组定义中使用0情况下工作的好处,并非所有编译器都支持,但有些支持。 如果分配基于offsetof() ,则无需担心可能的细节会使您的数学错误。

它的工作原理没有变化,struct是一个C99灵活的数组成员。

我会建议反对,因为可能的对齐问题而是考虑这个:

 struct my_struct { char *arr_space; unsigned int len; } struct my_struct *ptr = malloc(sizeof(struct my_struct) + 10); ptr->arr_space = ptr + 1; ptr->len = 10; 

这将为您提供局部性和安全性:)并避免奇怪的对齐问题。

通过对齐问题,我的意思是访问未对齐内存的访问延迟。

在原始示例中,如果添加一个字节或非字对齐的成员(byte,char,short),那么编译器可能会扩展结构的大小,但就指针而言,您正在读取内存后直接读取内存。 struct(非对齐)。 这意味着如果你有一个对齐类型的数组,比如int每次访问都会使你在读取未对齐内存时获得命中的CPU上的性能上升。

 struct { byte_size data; char *var_len; some_align added by compiler; } 

在原始情况下,您将从some_align区域读取,这只是填充物,但在我的情况下,您将从之后读取对齐的额外内存(这浪费了一些空间,但这通常没问题)。

这样做的另一个好处是,通过在一次分配中为struct可变长度成员分配所有空间而不是单独分配它们,可以从分配中获得更多的局部性(避免多个分配调用开销并为您提供一些缓存局部性而不是弹跳全部记忆)。

它是“正确的”,但你需要一个很好的理由来做一个更合理的解决方案。 更常见的是,您可能会使用此技术“覆盖”某些现有数组,以对其施加某种类型的标头结构。

请注意,GCC扩展允许零长度数组成员用于此目的,而ISO C99通过允许具有空括号的成员(仅作为最后成员)使该实践“合法化”。

请注意,存在一些语义问题 – 结构的大小当然不会占最终成员的“灵活”大小,并且“按值”传递结构只会传递标题和第一个元素(或者没有使用GCC的元素)扩展或C99灵活数组成员)。 类似地,直接结构分配不会复制所有数据。