【问题标题】:Assert the allocation of a variable-length array断言可变长度数组的分配
【发布时间】:2014-09-18 19:45:07
【问题描述】:

我为可能的重复道歉(无法找到答案):

是否需要确保变长数组的分配成功完成?

例如:

void func(int size)
{
    int arr[size];
    if (arr == NULL)
    {
        // Exit with a failure
    }
    else
    {
        // Continue as planned
    }
}

答案似乎很明显,但arr == NULL的语法感觉有点不寻常。

谢谢


更新:

我承认我还不确定上面的代码是否可以编译(假设可以编译)。

如果不编译,则表示无法断言变长数组的分配。

因此,我假设如果分配失败,那么程序会立即崩溃。

这将是一个非常尴尬的情况,因为程序在非法内存访问(读取或写入)后崩溃是有意义的,而不是在内存分配不成功后崩溃。

或者也许分配不会导致任何事情,但是一旦我在“落在”堆栈之外的条目处访问数组,我可能会遇到内存访问冲突(如堆栈溢出)......?

说实话,如果 VLA 后面有更多的局部变量(尤其是其他 VLA),我什至看不到 VLA 是如何在堆栈上分配的,所以我也希望能就这个问题得到解答。

【问题讨论】:

  • 局部变量在栈上分配。如果它不能分配它,程序可能会在你测试之前崩溃。
  • @Barmar:根据我在 VLA 上阅读的内容,该标准没有定义它们是在堆栈上分配还是在堆上分配。如果“那个东西”确实立即崩溃了,那么它就会使这种机制变得毫无用处。
  • 你说得对,标准并没有强制要求。但我希望这是大多数实现的方式。本地数组永远不能是NULL
  • @Deduplicator:这不是让 VLA 在所有可能的情况下都毫无用处吗?
  • 编译器魔法,还有什么?早期的 x86(可能是其他的)大多数时候除了堆栈指针之外还使用帧指针。如今,它尽可能地被省略了......所以,编译器可能会在那里恢复这个概念......

标签: c c99 variable-length-array


【解决方案1】:

这个问题源于一个稍有缺陷的第一个前提。您无法检查数组是否为NULL,因为就像一个流行的讨论主题一样,an array is not a pointer in C。数组就地存储对象。

如果没有分配数组,您将无法访问可以访问数组名称的代码。局部数组与任何其他局部变量完全相同:它的存在是固有的,并且假定周围的代码完全可以运行,并且语言中没有检查任何给定变量槽是否已被“分配”的概念(正如问题注释中的 cmets ,“堆栈”是低于 C 级操作的概念 - 语言假设它“发生”,通过未指定的魔法)。为了让代码在最基本的层面上有意义,它必须假设这总是成功的。

因此,在无法分配数组的情况下发生的情况与在运行时无法为任何其他局部变量分配空间时发生的情况相同 - 情况固有未定义且无法定义,因为违反了 C 语言抽象机的假设。该语言没有(完全正式的)概念甚至可以表达这一点,更不用说检查它或从中恢复,因此对其进行测试同样超出了范围。就像堆栈溢出一样,这基本上肯定会导致致命的崩溃。

不会使 VLA 无用,原因如下:

  1. VLA 的许多用途不会危及生命。也许变体的唯一用途是选择 3 到 5 之间的数字?这对于空间来说并不比使用更多的标量局部变量更糟糕。

  2. 正如避免无限递归要求程序员证明 C 编译器不具备的某些属性一样,您应该在设计程序时至少对 VLA 允许消耗的空间量有一个弱限制任何给定的时间。例如,您可以向自己证明,没有任何 VLA 函数是递归的,或者从递归函数调用的,并且它们使用的空间都不超过 eg 10K - 这非常有用并且应该是安全的。

  3. 您可以将 VLA 视为一种优化,以节省空间,否则您必须分配静态大小的本地数组(例如,在第一个示例中,始终分配 5而不是 3)。只要您知道并围绕静态上限进行设计,它们就可以有效地保证您的程序更安全不会溢出,方法是提供一个选项,在不使用时不总是使用尽可能多的空间需要。

【讨论】:

  • 我知道这个问题会让我遇到麻烦,因为人们试图解释“数组不是 C 中的指针”。您可以从语句“arr == NULL 的语法感觉有点不寻常”中推断出我很清楚这一点。但是 VLA 不是普通的数组。例如(正如我在问题末尾提到的),我很确定它以不同于“普通”数组的方式在堆栈上分配。
  • 此外,这里是维基百科上的一个链接,指出可以在堆上分配 VLA,在这种情况下,它本质上是一个指针(除非我误解了它的意思):en.wikipedia.org/wiki/Variable-length_array
  • 您混淆了语言及其实现。数组不是指针。是否在堆上分配本地数组并不重要——那是机器的事情。 C 不知道有一个堆(C 也不知道内存地址)。编译器选择插入多少层间接并不重要。从语言的抽象机 POV 来看,没有,这意味着该语言无法表达任何东西。
  • 好的,所以这是“当运行时无法为任何其他局部变量分配空间时会发生什么”,我对此表示怀疑:据我所知,在尝试访问之前什么都不会发生(读或写)这个局部变量被创建。与 VLA 有什么不同吗?换句话说,UB是在分配后立即发生,还是仅在尝试一次内存访问操作时发生?
  • 此外,局部变量分配不会产生任何额外的汇编代码。 VLA 有什么不同吗?如果没有编译器添加一些汇编代码,我真的无法想象如何分配 VLA。如果确实如此,那么 UB 是否会发生在该段代码中(即,尝试任何内存访问操作之前)?
猜你喜欢
  • 2013-05-16
  • 1970-01-01
  • 2014-03-04
  • 2012-03-01
  • 2012-05-04
  • 2011-11-30
  • 2023-03-30
  • 1970-01-01
  • 2021-07-11
相关资源
最近更新 更多