我们可以有一个可变长度数组类型的struct元素吗?

时间:2015-08-31 12:28:35

标签: c struct variable-length-array

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

条件如下:

typedef struct
{
   uint8_t No_Of_Employees;
   uint8_t Employee_Names[No_Of_Employees][15];
}st_employees;

5 个答案:

答案 0 :(得分:12)

如果在C99或C11中进行编码,您可能希望使用flexible array members(您没有给出明确的维度,但是您应该在运行时对其进行约定)。< / p>

 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)功能(作为练习留给读者)。它可能应该在ifreee->Employee_Names[i]循环,然后free(e); ......

不要忘记记录有关内存使用的约定(谁负责调用mallocfree)。详细了解C dynamic memory allocation(并且害怕memory fragmentationbuffer overflows以及其他任何undefined behavior。)

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

答案 1 :(得分:5)

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

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

  

结构或联合的成员可以具有除a之外的任何完整对象类型   可变修改类型。 [...]

并且,VLA是一种可变的修改类型。

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

  

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

所以,你可以做类似

的事情
typedef struct
{
   uint8_t No_Of_Employees;
   uint8_t* Employee_Names[];
}st_employees;

以后,您可以在运行时动态地将内存分配到Employee_Names(以及Employee_Names[i])并使用它。

答案 2 :(得分:1)

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

答案 3 :(得分:1)

NO,

定义结构时,必须确认其大小,以便在声明该结构类型的变量时,可以为该变量分配内存。

想想这个场景。如果要声明p类型的变量st_employees,由于尚未设置No_Of_Employees,因此未确认变量p的大小,因此变量无法分配。但是,如果不声明No_Of_Employees类型的变量,则无法设置st_employees。这是一个悖论。

答案 4 :(得分:0)

您可以使用动态分配执行此操作,如下所示:

#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;
}

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

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

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