断言变长数组的分配

3

很抱歉,可能会有重复(我没有找到答案):

我们需要确保变长数组的分配已经成功完成吗?

例如:

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

看起来很明显答案是“是”,但语法“arr == NULL”感觉有点不寻常。
谢谢
更新:
我承认我没有确保上面的代码是否可以编译(假设它可以)。
如果不能编译,则意味着无法断言变长数组的分配。
因此,我认为如果分配失败,则程序会立即崩溃。
这将是一个非常尴尬的情况,因为程序在非法内存访问(读取或写入)之后崩溃是有道理的,但在内存分配不成功后崩溃就不合适了。
或者分配不会导致任何问题,但是一旦我访问超出堆栈范围的条目时,我可能会遇到内存访问违规(如堆栈溢出)...?
老实说,如果有更多的本地变量跟随它们(特别是其他VLAs),我甚至看不到VLAs如何在堆栈上分配,因此我也希望在这个问题上得到答案。

@Barmar: 从我所读的有关可变长度数组的内容来看,标准并未定义它们是在堆栈还是堆上分配的。如果“那个东西”确实立即崩溃,那么这个机制就相当无用了。 - barak manos
你说得对,标准没有规定。但我认为大多数实现都是这样做的。本地数组永远不可能是“NULL”。 - Barmar
1
@Deduplicator:这不是在任何可能的情况下都使VLA变得毫无用处吗? - barak manos
不是的。它仍然有用,但它是一种带有自己危险性的强大工具。无论你如何到达那里,过度使用堆栈空间总是不好的。 - Deduplicator
2
编译器的魔法,还有什么?早期的x86(可能还有其他架构)大多数情况下除了栈指针之外还使用了帧指针。现在如果可能的话就省略了它...所以,编译器可能会在那里做一些恢复这个概念的魔法... - Deduplicator
显示剩余10条评论
1个回答

3
这个问题的出发点有些错误。你不能检查一个数组是否为NULL,因为正如许多讨论所指出的那样,在C中,数组不是指针。数组本身就是存储对象。
只有在分配了数组之后才能访问它的名称。一个本地数组与任何其他本地变量完全相同:其存在是固有的,并且被假定对于周围的代码来说是运行的,语言中没有任何概念可以检查是否已经"分配"了任何给定的变量槽(正如问题的评论所指出的,"堆栈"是C操作的下一级概念,语言假定它通过未指定的魔术"发生")。为了使代码在最基本的层面上有意义,它必须始终假定这一点成功。
当数组无法分配时,情况与运行时无法为任何其他本地变量分配空间的情况相同——这种情况本质上是未定义和不可定义的,因为C语言抽象机器所做的假设被违反了。该语言没有(完全正式的)概念甚至可以表达这一点,更不用说检查或从中恢复,因此测试它同样不在范围内。就像堆栈溢出一样,这基本上肯定会导致致命崩溃。
这并不意味着可变长度数组(VLA)是无用的,原因如下:
1. 许多可变长度数组的用途不会非常大。也许唯一的变化只是选择3到5之间的数字?这对于空间来说不比使用更多标量局部变量更糟糕。 2. 就像避免无限递归需要程序员证明某些属性一样,同样你应该设计你的程序,至少对VLAs将在任何给定时间消耗的空间量有一个弱界限。例如,你可以向自己证明没有VLA函数是递归的,或者从递归函数调用,而且它们中没有一个使用超过e.g. 10K的空间 - 这非常有用,并且应该是安全的。 3. 你可以将VLAs视为一种优化,以允许你在必须要分配静态大小的本地数组时(例如,在第一个示例中,总是分配5而不是3),节省空间。只要你知道并设计了静态上限,它们就有效地保证使你的程序在不需要时不总是使用太多空间,从而使你的程序更加安全。

此外,这里有一个维基百科链接,说明VLA可以在堆上分配,这种情况下它本质上是一个指针(除非我误解了它的意思):http://en.wikipedia.org/wiki/Variable-length_array。 - barak manos
你把语言和它的实现弄混了。数组不是指针。局部数组是否在堆上分配并不重要 - 这是机器的事情。C语言不知道有堆(C也不知道内存地址)。编译器选择插入多少层间接性无关紧要;从语言的抽象机器POV来看,没有任何层级,这意味着该语言无法表达任何层级。 - Alex Celeste
好的,所以我对“当运行时无法为任何其他本地变量分配空间时会发生什么”有疑问:就我所知,除非尝试访问(读取或写入)此本地变量,否则不会发生任何事情。使用VLA是否有所不同?换句话说,UB是在分配后立即发生,还是只有在尝试进行内存访问操作时才会发生? - barak manos
此外,局部变量分配不会产生任何额外的汇编代码。对于可变长度数组(VLAs)是否有所不同?我无法想象VLA如何在编译器没有添加一些汇编代码的情况下进行分配。如果确实是这种情况,那么在该代码片段中是否会发生未定义行为(即,在尝试进行任何内存访问操作之前)? - barak manos
...但是^是segfault发生的时候... UB是另一种语言抽象。正如上面所示,根据您如何分配堆栈空间,事情可能会在多个不同的点出错。 - Alex Celeste
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接