如何在C中包含动态数组INSIDE结构?

时间:2010-01-13 23:02:47

标签: c pointers arrays struct memory-management

我环顾四周,但一直无法找到解决问题的问题。 这是我的代码:

 #include <stdlib.h>

struct my_struct {
    int n;
    char s[]
};

int main()
{
    struct my_struct ms;
    ms.s = malloc(sizeof(char*)*50);
}

这是gcc给我的错误: 错误:无效使用灵活的数组成员

如果我在结构中声明s的声明为

,我可以编译它
char* s

这可能是一个优秀的实现(指针算法比数组快,是吗?) 但我在c中声明了

的声明
char s[]

相同
char* s

7 个答案:

答案 0 :(得分:66)

你现在写的方式,曾被称为“struct hack”,直到C99祝福它为“灵活的阵列成员”。您收到错误(可能无论如何)的原因是它需要后面跟一个分号:

#include <stdlib.h>

struct my_struct {
    int n;
    char s[];
};

为此分配空间时,您希望为结构加上分配所需的数据空间大小:

struct my_struct *s = malloc(sizeof(struct my_struct) + 50);

在这种情况下,灵活的数组成员是一个char数组,而sizeof(char)== 1,所以你不需要乘以它的大小,但就像你需要的任何其他malloc一样它是一个其他类型的数组:

struct dyn_array { 
    int size;
    int data[];
};

struct dyn_array* my_array = malloc(sizeof(struct dyn_array) + 100 * sizeof(int));

编辑:这会将成员更改为指针,从而产生不同的结果。在这种情况下,您(通常)需要两个单独的分配,一个用于结构本身,另一个用于指针指向的“额外”数据。使用灵活的数组成员,您可以在一个块中分配所有数据。

答案 1 :(得分:19)

你需要先决定你要做的是什么。


如果你想要一个带有指向[独立]数组的指针的结构,你必须将它声明为

struct my_struct { 
  int n; 
  char *s;
}; 

在这种情况下,您可以以任何方式创建实际的struct对象(例如自动变量)

struct my_struct ms;

然后单独为数组分配内存

ms.s = malloc(50 * sizeof *ms.s);  

实际上,通常不需要动态分配数组内存

struct my_struct ms;
char s[50];

ms.s = s;

这完全取决于您需要从这些对象中获得什么样的生命周期。如果你的结构是自动的,那么在大多数情况下,数组也是自动的。如果struct对象拥有数组内存,那么其他方面就没有意义了。如果结构本身是动态的,那么数组通常也应该是动态的。

请注意,在这种情况下,您有两个独立的内存块:struct和array。


完全不同的方法是使用“struct hack”成语。在这种情况下,数组成为结构的组成部分。两者都驻留在单个内存块中。在C99中,结构将声明为

struct my_struct { 
  int n; 
  char s[];
}; 

并创建一个对象,你必须动态分配整个事物

struct my_struct *ms = malloc(sizeof *ms + 50 * sizeof *ms->s);

计算此情况下的内存块大小以容纳struct成员和运行时大小的尾随数组。

请注意,在这种情况下,您无法选择创建静态或自动对象等结构对象。末尾具有灵活数组成员的结构只能在C中动态分配。


关于指针aritmetics的假设比数组更快是完全错误的。根据定义,数组通过指针算术运算,因此它们基本相同。而且,真正的数组(不衰减到指针)通常比指针对象快一点。必须从内存中读取指针值,而数组在内存中的位置是从数组对象本身“已知”(或“计算”)。

答案 2 :(得分:1)

只允许在结构的末尾使用未指定大小的数组,并且只能在某些编译器中使用。它是一个非标准的编译器扩展。 (虽然我想我记得C ++ 0x会允许这样做。)

尽管数组不是单独的分配。因此,您需要分配所有my_struct,而不仅仅是数组部分。

我所做的只是给阵列一个小而非零的大小。对于字符数组通常为4,对于wchar_t数组通常为2以保持32位对齐。

然后,在进行分配时,可以考虑数组的声明大小。我经常不认为slop小于堆管理器在任何情况下工作的粒度。

另外,我认为您不应该在分配中使用sizeof(char *)。

这就是我要做的。

struct my_struct {
    int nAllocated;
    char s[4]; // waste 32 bits to guarantee alignment and room for a null-terminator
};

int main()
{
    struct my_struct * pms;
    int cb = sizeof(*pms) + sizeof(pms->s[0])*50;
    pms = (struct my_struct*) malloc(cb);
    pms->nAllocated = (cb - sizoef(*pms) + sizeof(pms->s)) / sizeof(pms->s[0]);
}

答案 3 :(得分:0)

数组将解析为指针,此处必须s定义为char *s。结构基本上是一个容器,并且必须(IIRC)是固定大小的,因此在其内部具有动态大小的数组是不可能的。因为你无论如何malloc记忆,这对你所追求的事情没有任何影响。

基本上你说,s表示内存位置。请注意,您稍后仍可使用s[0]等符号来访问此页。

答案 4 :(得分:0)

  

指针算法比数组快,是吗?

完全没有 - 他们实际上是一样的。数组在编译时转换为指针算术。

char test[100];
test[40] = 12;

// translates to: (test now indicates the starting address of the array)
*(test+40) = 12;

答案 5 :(得分:0)

我怀疑编译器不知道为s []分配多少空间,如果你选择用它声明一个自动变量。

我同意Ben所说的,声明你的结构

struct my_struct {
    int n;
    char s[1];
};

另外,为了澄清他对存储的评论,声明char *s不会将结构放在堆栈上(因为它是动态分配的)并在堆中分配s,它将做的是将数组的第一个sizeof(char *)字节解释为指针,这样您就不会对您认为的数据进行操作,并且可能会致命。

至关重要的是要记住,尽管指针和数组上的操作可能以相同的方式实现,但它们并不是一回事。

答案 6 :(得分:-6)

生成的代码将是相同的(数组和ptr)。除了数组之外不会编译

的事实

和BTW - 做c ++并使用vector