如何在C语言中将动态数组包含在结构体内部?

55

我找了一圈,但一直没有找到必定已经被问过的问题的解决方案。 这是我的代码:

 #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
char s[] 在函数参数列表中的作用和 char *s 是一样的,只是写法不同。 - Alok Singhal
8个回答

86
你现在的写法,以前被称为"结构体技巧",直到C99将其赋予了"灵活数组成员"的称号。你之所以会收到错误的提示信息(很可能是这个原因),是因为需要在最后加上分号:
#include <stdlib.h>

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

当你分配内存时,你需要为结构体分配的空间大小加上数组所需的空间大小:

注:原文中使用了斜体强调"plus"一词,请按要求保留标签。

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
C99实际上允许了这个 - 哎呀! - Martin Beckett
3
哇,我从没见过这个... 这是依赖于在结构体中“柔性数组成员”被声明为最后一个字段吗?每个结构体只能有一个“柔性数组成员”吗? - vicatcu
@vicatcu:是的,对于你的两个问题都是。如果我没记错的话,如果你嵌入了一个包含可变数组成员的结构体,它必须是外部结构体中的最后一个成员,因此当它们全部组合在一起时,可变数组成员始终是最后一个项目。 - Jerry Coffin

28

首先,您需要决定您要做什么。


如果您想要一个带有指向独立数组的指针的结构体,请将其声明为

struct my_struct { 
  int n; 
  char *s;
}; 
在这种情况下,你可以以任何方式创建实际的结构体对象(例如自动变量)。
struct my_struct ms;

然后独立分配数组的内存

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

实际上,通常没有必要动态分配数组内存。

struct my_struct ms;
char s[50];

ms.s = s;

这完全取决于你需要这些对象有何种生存周期。如果你的结构体是自动的,那么在大多数情况下,数组也将是自动的。如果结构体对象拥有数组内存,那么没有必要采用其他方式。如果结构体本身是动态的,则数组通常也应该是动态的。

请注意,在这种情况下,你有两个独立的内存块:结构体和数组。


另一种完全不同的方法是使用“结构体尺寸技巧”。在这种情况下,数组成为结构体的一个组成部分。两者都存在于单个内存块中。在C99中,结构体的声明方式如下:

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

而要创建一个对象,您必须动态地分配整个对象

struct my_struct *ms = malloc(sizeof *ms + 50 * sizeof *ms->s);
在这种情况下,内存块的大小被计算以容纳结构体成员和运行时大小的尾随数组。
请注意,在此情况下,您无法选择创建静态或自动对象等结构体对象。在C中,只能动态分配具有灵活数组成员的结构体。
你对指针运算比数组更快的假设是完全错误的。按定义,数组通过指针运算工作,因此它们基本上是相同的。此外,一个真正的数组(而不是衰减到指针)通常比指针对象稍快一些。必须从内存中读取指针值,而数组在内存中的位置是从数组对象本身“已知”(或“计算”的)。

UV为sizeof *ms + 50 * sizeof *ms->s:更易于审查和维护。 - chux - Reinstate Monica

1

在结构的末尾,只允许使用未指定大小的数组,并且仅适用于某些编译器。这是一种非标准的编译器扩展。(虽然我记得C++0x将允许此操作。)

但是,该数组不会与结构分开分配。因此,您需要分配所有的my_struct,而不仅仅是数组部分。

我所做的就是给数组一个小但非零的大小。通常为字符数组的4个字节和wchar_t数组的2个字节,以保持32位对齐。

然后,在进行分配时,您可以考虑数组的声明大小。我经常不这样做,因为堆管理器的粒度比余量更小。

此外,我认为您不应该在分配中使用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]);
}

1

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

我同意Ben所说的,声明您的结构体。

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

此外,为了澄清他对存储的评论,声明char *s不会将结构体放在堆栈上(因为它是动态分配的),并在堆中分配s,它将解释数组的前sizeof(char *)个字节作为指针,所以您将不会操作您认为的数据,并且可能会导致致命错误。
请记住,尽管指针和数组的操作可能以相同的方式实现,但它们并不是相同的东西。

0

在C语言中存储数组于结构体内的工作代码,以及如何存储数组元素的值。如果您有任何疑问,请留下评论,我会尽力澄清。

结构体定义:

struct process{
    int process_id;
    int tau;
    double alpha;
    int* process_time;
};

进程结构的内存分配:

 struct process* process_mem_aloc = (struct process*) malloc(temp_number_of_process * sizeof(struct process));

循环遍历多个进程,对于每个进程更新 process_time 动态数组

int process_count = 0;
int tick_count = 0;


while(process_count < number_of_process){


  //Memory allocation for each array of the process, will be containting size equal to number_of_ticks: can hold any value

        (process_mem_aloc + process_count)->process_time = (int*) malloc(number_of_ticks* sizeof(int));

从文件逐行读取数据,存储到process_time数组中,然后从存储的值中打印出来,接下来的while循环在process while循环内部

        while(tick_count < number_of_ticks){

            fgets(line, LINE_LENGTH, file);
            *((process_mem_aloc + process_count)->process_time + tick_count) = convertToInteger(line);;
            printf("tick_count : %d , number_of_ticks %d\n",tick_count,*((process_mem_aloc + process_count)->process_time + tick_count));
            tick_count++;
        }

        tick_count = 0;

0
指针算术比数组快,对吗?
并不是这样的 - 实际上它们是相同的。数组在编译时转换为指针算术。
char test[100];
test[40] = 12;

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

0

数组将解析为指针,在这里你必须将 s 定义为 char *s。结构体基本上是一个容器,必须(如果我没记错的话)是固定大小的,因此在其中放置动态大小的数组是不可能的。既然你已经使用了 malloc 分配内存,那么这对你想要实现的功能不会有任何影响。

基本上你是在说,s 将指示一个内存位置。请注意,您仍然可以使用类似 s[0] 的符号表示法稍后访问它。


-8

生成的代码将是相同的(数组和指针)。除了数组无法编译之外。

顺便说一句 - 用C++并使用向量。


7
建议我使用C++和向量根本不是一种建设性的建议。你可以直接说:雇用一名软件工程师为您编写程序。 - Tom
生成的代码将远非相同。数组并不是指针。将数组嵌入结构或从结构指向数组是两码事。 - AnT stands with Russia
是的,您说得对,它们并不相同。我想表达的是,在代码处理作为 foo * 或 foo [] 传递给它的内容时,代码将是相同的。本质上,没有性能差异。 - pm100
汤姆,有些人不知道STL、C++向量等。我试图发布一些与众不同的内容。如果您认为这样没有帮助,我很抱歉。 - pm100

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接