我们可以有一个可变长度数组类型的结构元素吗? [复制]

Posted

技术标签:

【中文标题】我们可以有一个可变长度数组类型的结构元素吗? [复制]【英文标题】:Can we have a struct element of type Variable length array? [duplicate] 【发布时间】:2015-11-25 11:44:40 【问题描述】:

我们可以声明一个可变长度的结构元素吗?

条件如下:

typedef struct

   uint8_t No_Of_Employees;
   uint8_t Employee_Names[No_Of_Employees][15];
st_employees;

【问题讨论】:

你的编译器是怎么说的? 您可以做的是将数组声明为灵活数组成员,稍后您可以在运行时为其分配空间。来自 C 标准的示例:struct s int n; double d[]; ; int m = /* some value */; struct s *p = malloc(sizeof (struct s) + sizeof (double [m])); 我正在使用 IAR gcc 编译器 GCC 编译器的哪个版本?什么编译命令行?你用gcc -std=c99 -Wall编译了吗? @user12345 除非我一直生活在岩石之下,否则 IAR 和 GCC 是截然不同的编译器。出于好奇,目标处理器是哪一个? 【参考方案1】:

如果在 C99 或 C11 中编码,您可能希望使用 flexible array members(您没有给出明确的维度,但您应该在运行时有一个关于它的约定)。

 typedef struct 
    unsigned No_Of_Employees;
    char* Employee_Names[]; // conventionally with No_of_Employees slots
 st_employees;

对于任何数组,灵活数组成员的每个槽都有固定的大小。我正在使用指针(例如,我的 Linux/x86-64 机器上的 8 个字节)。

(在 C99 标准之前的旧编译器中,您可以尝试提供 0 维度,例如 char* Employee_Names[0];,即使它违反标准)

然后您将使用例如分配这样的结构

 st_employees* make_employees(unsigned n) 
    st_employees* s = malloc(sizeof(s_employees)+n*sizeof(char*));
    if (!s)  perror("malloc make_employees"); exit(EXIT_FAILURE); ;
    s->No_of_Employees = n;
    for (unsigned i=0; i<n; i++) s->Employe_Names[i] = NULL;
    return s;
 

你可能会使用它(strdup(3) 在堆中复制一个字符串)

 st_employees* p = make_employees(3);
 p->Employee_Names[0] = strdup("John");
 p->Employee_Names[1] = strdup("Elizabeth");
 p->Employee_Names[2] = strdup("Brian Kernighan");

您需要一个void destroy_employee(st_employee*e) 函数(留给读者作为练习)。它可能应该在每个e-&gt;Employee_Names[i] 循环ifree,然后是free(e);...

不要忘记记录有关内存使用的约定(谁负责调用mallocfree)。阅读有关C dynamic memory allocation 的更多信息(并害怕memory fragmentation 和buffer overflows 以及任何其他undefined behavior)。

如果使用早于 GCC 5 的 GCC,请务必使用 gcc -std=c99 -Wall 进行编译,因为旧 GCC 4 编译器的默认标准是 C89。对于较新的编译器,请询问所有警告以及更多警告,例如gcc -Wall -Wextra...

【讨论】:

我是一名嵌入式程序员。我正在使用 IAR 编译器,它给出的错误是:表达式必须有一个常量值。我启用了 C99 标准和 VLA。 那么您应该将您的编译器升级到符合 C99 的编译器。试试GCC 我使用的是 C99 标准并启用了 VLA 那么您应该考虑升级您的GCC,或许可以通过下载其源代码(用于GCC 5)并从源代码构建编译器。 @user12345 可能是IAR不支持strdup,这是一个非标准函数。您可以用 malloc + strcpy 替换 strdup。【参考方案2】:

TL;DR 回答 - 不,你不能。

为了详细说明,让我引用C11,第 §6.7.2.1 章,Structure and union specifiers强调我的

结构或联合的成员可以有任何完整的对象类型除了 可变修饰类型。 [...]

而且,VLA 是可变修改类型。

但是,引用同一标准,关于flexible array member

作为一种特殊情况,具有多个命名成员的结构的最后一个元素可能 数组类型不完整;这称为灵活数组成员。 [...]

所以,你可以做类似的事情

typedef struct

   uint8_t No_Of_Employees;
   uint8_t* Employee_Names[];
st_employees;

以后,您可以在运行时将内存动态分配给Employee_Names(以及Employee_Names[i])并加以利用。

【讨论】:

【参考方案3】:

没有,

当你定义一个结构体时,必须确认它的大小,这样当你声明一个该结构体类型的变量时,就可以为该变量分配内存。

想想这个场景。当您要声明st_employees 类型的变量p 时,由于尚未设置No_Of_Employees,因此未确定变量p 的大小,因此无法为该变量分配内存。但是你不能在不声明st_employees 类型的变量的情况下设置No_Of_Employees。这是一个悖论。

【讨论】:

【参考方案4】:

据我了解,这是不可能的;用不同的字段定义结构的一个字段是不可能的。

【讨论】:

【参考方案5】:

您可以通过如下方式进行动态分配:

#include <stdio.h>
#include <stdlib.h>

#define SIZE_OF_ELEM 15
#define uint8_t char 
typedef struct

   uint8_t No_Of_Employees;
   uint8_t **Employee_Names;
st_employees;

int main()

    int i;
    st_employees emps;
    emps.No_Of_Employees = 2; //number of elements
    // allocate the number of elements
    emps.Employee_Names = malloc(emps.No_Of_Employees);
    for (i=0; i < emps.No_Of_Employees; i++)
    
        // allocate each element
        emps.Employee_Names[i] = malloc(SIZE_OF_ELEM);
        // fill the element with some data
        sprintf(emps.Employee_Names[i], "emp_n%d", i);
    
    // show the content
    for (i=0; i<emps.No_Of_Employees; i++)
    
        printf("Employee %d content: %s\n", i, emps.Employee_Names[i]);
    
    return 0;

当然这是一个例子,你必须检查分配,精确类型的大小和释放内存。

请注意,此方法允许创建可以具有不同类型的对象集合,并且无需使用任何特定的 C 编译器版本或选项。

但是,在非常基本的情况下(如在 OP 示例中),它不是更好的解决方案,因为它会分割内存(每个对象分配一次)。所以请谨慎使用。

【讨论】:

不,不要这样做,绝对没有理由将结构的内存碎片化到整个堆中。而是将Employee_Names 声明为灵活的数组成员,并同时在相邻内存中为结构+ 数组的大小分配空间。来自 C 标准的示例:struct s int n; double d[]; ; int m = /* some value */; struct s *p = malloc(sizeof (struct s) + sizeof (double [m])); 我不同意。这是一种灵活的方式。它允许您使用任何类型的数据。在该特定示例中,这不是更好的方法,但问题是使用可变结构长度数据,并且示例显示了特定大小的元素。 我的解决方案应该有效。我猜OP忘记了-std=c99标志到GCC;而且您的解决方案确实使内存碎片化了很多,这是嵌入式软件中的一个问题。 @daouzli 我投了反对票,因为你宣扬了不好的做法:堆的不必要的碎片(假数组),而不是在相邻的内存中分配所有东西(真正的数组)。除了堆碎片之外,您的方法还会导致您的程序变得不必要的缓慢,而没有任何收获。不仅因为行间有各种表查找开销指令,更重要的是因为相邻的内存单元意味着您的程序可以更好地利用片上数据缓存内存。 @user12345 它可以工作,但它不是最理想的,也是不好的做法。举一个现实生活中的傻例子:你可以决定把你的饼干放在饼干罐里,或者你可以在房子的每个房间里放一个饼干。两种方法都存储 cookie。后一种方法涉及到到处跑来跑去搜索,每次你想要一个 cookie 时。而不是仅仅从饼干罐中的预期位置取出一个,所有其他饼干都在那里。

以上是关于我们可以有一个可变长度数组类型的结构元素吗? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

我们可以在 C++ 中创建可变长度数组吗

vector的使用方法

集合框架

我们可以在 c++20 协程中使用 alloca() 或可变长度数组扩展吗?

javaSE集合---进度2

stringbuffer和stringbuilder