C99如何处理在运行时无法创建可变长度数组的问题?

C99如何处理在运行时无法创建可变长度数组的问题?,c,c99,variable-length-array,C,C99,Variable Length Array,我为嵌入式系统编程。一条金科玉律是我们从不调用malloc();所有数据必须在编译时静态分配 因此,我并不十分熟悉C99引入的 这个概念似乎很清楚,我不需要解释。我的问题是,如果这样一个数组没有足够的可用内存,那么在运行时会发生什么 我可以想象,它依赖于o/s,可能依赖于编译器,GCC/Linux和Windows上的MS visual Studio C会做什么?任何X99或Posix定义?从标准的角度来看,尝试分配VLA的大小无法满足实现的要求会调用未定义的行为。因为该标准没有提供发现实现可以安

我为嵌入式系统编程。一条金科玉律是我们从不调用malloc();所有数据必须在编译时静态分配

因此,我并不十分熟悉C99引入的

这个概念似乎很清楚,我不需要解释。我的问题是,如果这样一个数组没有足够的可用内存,那么在运行时会发生什么


我可以想象,它依赖于o/s,可能依赖于编译器,GCC/Linux和Windows上的MS visual Studio C会做什么?任何X99或Posix定义?

从标准的角度来看,尝试分配VLA的大小无法满足实现的要求会调用未定义的行为。因为该标准没有提供发现实现可以安全创建的数组大小的方法,也没有要求实现允许任何特定大小,任何创建大小大于1的VLA对象的尝试都应被视为调用未定义的行为,除非对实现的内部工作有足够的了解,以确定它能够处理的VLA的大小

如果malloc()不可用,最好是定义一个大型 无论哪种类型具有最粗略的对齐要求,都将其地址存储到
volatile
限定指针[指针本身所在的存储器] 应该如此限定]读回它,并将其解释为 内存池。不应对原始数组对象进行其他用途。虽然 该标准不能保证编译器不会决定生成检查指针是否仍然标识原始对象的代码,如果仍然标识原始对象,则跳过使用该指针访问原始对象类型以外的任何内容的代码,在指针上使用
volatile
,应该不会出现这种情况

创建内存池后,您可以编写自己的内存管理 函数来使用它,尽管指针返回到池时 可能需要使用
volatile
-指针清洗黑客来防止 编译器避免使用基于类型的别名来证明处理最后的使用是正确的 作为旧类型的存储,相对于
作为新类型的存储。

可变长度数组通常在堆栈上分配。与在堆栈上分配的任何其他变量一样,这通常是通过从堆栈指针中减去(或为向上增长的堆栈向上添加)来完成的。可能会使用帧指针,以便函数在堆栈指针发生动态变化时跟踪其堆栈帧。与其他堆栈分配一样,此过程中通常没有错误检查

这带来了一些危险

  • 分配给堆栈的空间可能溢出。根据平台的不同,这可能导致内核出现某种内存错误,可能导致平台动态分配更多堆栈空间,也可能导致覆盖其他内存
  • 在使用堆栈之外的保护页进行自动堆栈增长和/或检测堆栈溢出的平台上,足够大的堆栈分配可能会“跳过”这些页面。在通常会捕获堆栈溢出的情况下,这可能会导致内存保护错误或更严重的内存损坏

  • 这两种风险都不是可变长度数组特有的,但可变长度数组使它们更容易隐藏,直到使用特定参数调用代码。

    所有数据必须在编译时静态分配意味着“所有数据必须在编译时静态分配”。不是“只要您使用自己的函数,您就可以在运行时分配自己的数据”。@n.m.:数组将在编译时分配;用户函数只会返回指向已分配存储的部分的指针。“分配”并不意味着“向操作系统、编译器或上帝请求任何资源”。这不一定是一个单一层面的事件。编译器为您的程序分配一块内存(静态),然后您的内存管理器使用该块来分配较小的块(动态)supercat您基本上是说,如果您想要一种动态分配,请不要使用VLA或malloc(它们不安全)。相反,静态地分配您知道在编译时是安全的资源(您知道平台限制),然后为该资源编写您自己的“TLB”版本并进行管理。谁承诺过大小1一定会成功?我不相信他,我想你已经回答了你自己的问题。这将导致未定义的行为,因此您需要了解您开发的特定平台如何处理此问题。我猜在调用使用VLA的函数之前,您必须先尝试并捕获它。我想,如果要避免malloc,VLAs也应该如此。适用于任何堆栈溢出的规则都是一样的,可能会在较小的嵌入式MCU上失控崩溃,并通过较大设备(包括Linux/Windows)上的内存保护进行捕获。更重要的是,堆栈分配也是动态分配,因此在C语言中是无法避免的。如果您避免使用函数指针/递归/VLAs/alloca,那么最好使用支持计算程序调用图堆栈上限的嵌入式编译器(或者提供适当的手动提示,比如可能的函数指针目标列表)。doynax,我可能错了,但在许多平台/环境中,堆栈在编译时每个进程都是固定的。例如,在windows上很容易耗尽堆栈(但这并不意味着它不是动态的,只是上限是静态的),但如果说可以通过使用任意堆栈空间启动新线程来解决此问题。@cdcdcd:是的,但仅限于