如何在 C 中包含一个可变大小的数组作为结构成员?

How to include a variable-sized array as stuct member in C?

我必须说,我对一个看似简单的问题有很大的困惑。我有一个结构,我想在其中将一个数组存储为一个字段。我想在不同的上下文中重用这个结构,有时我需要一个更大的数组,有时需要一个更小的数组。 C 禁止使用可变大小的缓冲区。因此,自然的方法是将指向此数组的指针声明为结构成员:

struct my {
    struct other* array;
}

然而,这种方法的问题是我必须遵守禁止动态内存分配的 MISRA-C 规则。那么如果我想分配内存并初始化数组,我不得不这样做:

var.array = malloc(n * sizeof(...));

这是 MISRA 标准所禁止的。我还能怎么做?

编辑:此解决方案不符合 MISRA-C 规则。

您可以某种在结构定义中包含 VLA,但前提是它在函数内部。解决此问题的一种方法是在主结构的末尾使用 "flexible array member",如下所示:

#include <stdio.h>
struct my {
  int len;
  int array[];
};

您可以创建在此结构上运行的函数。

void print_my(struct my *my) {
  int i;
  for (i = 0; i < my->len; i++) {
    printf("%d\n", my->array[i]);
  }
}

然后,要创建此结构的可变长度版本,您可以在函数主体中创建一种新类型的结构,包含您的 my 结构,但也定义该缓冲区的长度。这可以使用不同的大小参数来完成。然后,对于您调用的所有函数,您只需传递一个指向包含的 struct my 值的指针,它们就会正常工作。

void create_and_use_my(int nelements) {
  int i;

  // Declare the containing struct with variable number of elements.
  struct {
    struct my my;
    int array[nelements];
  } my_wrapper;

  // Initialize the values in the struct.
  my_wrapper.my.len = nelements;
  for (i = 0; i < nelements; i++) {
    my_wrapper.my.array[i] = i;
  }

  // Print the struct using the generic function above.
  print_my(&my_wrapper.my);
}

您可以使用 nelements 的任何值调用此函数,它会正常工作。这需要 C99,因为它确实使用 VLA。此外,还有一些 GCC extensions 让这更容易一些。

重要提示: 如果您将 struct my 传递给另一个函数,而不是指向它的指针,我可以向您保证它会导致各种错误,因为它不会用它复制可变长度数组。

这里的想法可能完全不适合你的情况,但考虑到你的限制,我不确定还有什么办法可以处理它。

创建一个大型静态数组并将其用作您的 "heap":

static struct other heap[SOME_BIG_NUMBER];

然后你会"allocate"从这个"heap"中得到记忆,就像这样:

var.array = &heap[start_point];

您必须做一些簿记工作以跟踪您的 "heap" 的哪些部分已被分配。这假定您对可执行文件的大小没有任何主要限制。

由于您使用的是 MISRA-C,我猜该软件在某种程度上是关键任务,在这种情况下,所有内存分配都必须是确定性的。堆分配被所有安全标准禁止,不仅是 MISRA-C,还有更通用的安全标准(IEC 61508、ISO 26262、DO-178 等)。

在这样的系统中,您必须始终针对最坏的情况进行设计,这将消耗最多的内存。您需要恰好分配那么多 space,不多也不少。在这样的系统中,其他一切都没有意义。

鉴于这些先决条件,您必须分配大小为 LARGE_ENOUGH_FOR_WORST_CASE 的静态缓冲区。一旦你意识到这一点,你只需要找到一种方法来跟踪你在这个缓冲区中存储了什么样的数据,通过使用一个枚举或者一个 "size used" 计数器。


请注意,不仅 malloc/calloc,而且 VLA 和灵活数组成员也被 MISRA-C:2012 禁止。如果您使用的是 C90/MISRA-C:2004,则没有 VLA,也没有任何明确定义的灵活数组成员的使用 - 它们在 C99 之前调用了未定义的行为。