【问题标题】:How to include a variable-sized array as stuct member in C?如何在 C 中包含可变大小的数组作为结构成员?
【发布时间】:2015-04-07 14:03:43
【问题描述】:

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

struct my {
    struct other* array;
}

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

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

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

【问题讨论】:

  • "C 禁止使用可变大小的缓冲区" ,只是 ANSI-C 或 C89 禁止这样做。 VLA(可变长度数组)从 C99 开始存在
  • 对。但无论如何,我不能在结构定义中这样设置它,因为它无论如何都无法访问该变量。
  • 在编译时是否知道不同的大小(n)?
  • 不,它实际上是必需,而不是建议限制。
  • MIRA 确实允许指针不是吗,你能有一个指向 VLA var.array = a[n] 的指针吗?

标签: c arrays static malloc misra


【解决方案1】:

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

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

鉴于这些先决条件,您必须分配大小为LARGE_ENOUGH_FOR_WORST_CASE 的静态缓冲区。一旦你意识到这一点,你只需要找到一种方法来跟踪你存储在这个缓冲区中的数据类型,方法是使用一个枚举,也可能是一个“使用的大小”计数器。


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

【讨论】:

    【解决方案2】:

    编辑:此解决方案不符合 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("%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 传递给另一个函数,而不是指向它的指针,我几乎可以保证它会导致各种错误,因为它不会复制带有它的可变长度数组。

    【讨论】:

    • 我刚刚对这个答案进行了重大修改——如果在阅读时把你搞砸了,很抱歉。
    • 灵活数组成员以及 VLA 也被 MISRA 禁止。 (MISRA-C 2012 18.7 和 18.8),所以这没有解决任何问题。
    • @Lundin 感谢您指出这一点,我已经编辑了我的答案以引起人们的注意。
    【解决方案3】:

    这里的想法可能完全不适合您的情况,但考虑到您的限制,我不确定如何处理它。

    创建一个大型静态数组并将其用作您的“堆”:

    static struct other heap[SOME_BIG_NUMBER];
    

    然后,您将从这个“堆”中“分配”内存,如下所示:

    var.array = &heap[start_point];
    

    您必须做一些簿记来跟踪您的“堆”的哪些部分已被分配。这假设您对可执行文件的大小没有任何主要限制。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-08-19
      • 1970-01-01
      • 1970-01-01
      • 2021-08-09
      • 1970-01-01
      • 2016-09-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多