我必须说,在一个看似基本的问题上,我有一个相当大的难题。我有一个结构,我想在其中存储一个数组作为字段。我想在不同的上下文中重用这个结构,有时我需要一个更大的数组,有时需要一个更小的数组。C禁止使用可变大小的缓冲区。因此,自然的方法是将指向该数组的指针声明为结构成员:
struct my {
struct other* array;
}
然而,这种方法的问题是,我必须遵守MISRA-C的规则,它禁止动态内存分配。因此,如果我想分配内存并初始化数组,我必须执行以下操作:
var.array = malloc(n * sizeof(...));
这被MISRA标准所禁止。否则我该怎么做?
由于您正在关注MISRA-C,我猜该软件在某种程度上是任务关键型的,在这种情况下,所有内存分配都必须是确定性的。堆分配被所有安全标准禁止,不仅是MISRA-C,还有更通用的安全标准(IEC 61508、ISO 26262、DO-178等)。
在这样的系统中,您必须始终针对最坏的情况进行设计,这将消耗最多的内存。你需要准确地分配那么多空间,不多也不少。在这样的体系中,其他一切都没有意义。
考虑到这些先决条件,您必须分配一个大小为LARGE_ENOUGH_FOR_WORST_CASE
的静态缓冲区。一旦你意识到了这一点,你只需要找到一种方法来跟踪你在这个缓冲区中存储了什么样的数据,通过使用枚举和"使用的大小"计数器。
请注意,MISRA-C:2012不仅禁止malloc/caloc,还禁止VLA和灵活阵列成员。如果您使用的是C90/MISRA-C:2004,则没有VLA,也没有任何定义良好的灵活数组成员使用——它们在C99之前调用了未定义的行为。
编辑:此解决方案不符合MISRA-C规则
您可以类型的在结构定义中包含VLA,但仅当它位于函数内部时。解决这个问题的一种方法是在主结构的末尾使用一个"灵活的数组成员",如下所示:
#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("%dn", 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扩展使这一点更容易。
重要信息:如果您将struct my
传递给另一个函数,而不是它的指针,我可以向您保证它会导致各种错误,因为它不会复制可变长度数组。
这里有一个想法可能完全不适合您的情况,但考虑到您的限制,我不确定如何处理它。
创建一个大型静态数组并将其用作"堆":
static struct other heap[SOME_BIG_NUMBER];
然后,您将从这个"堆"中"分配"内存,如下所示:
var.array = &heap[start_point];
您将不得不做一些记账来跟踪您的"堆"的哪些部分已被分配。这假设您对可执行文件的大小没有任何主要限制。