4

C 标准定义了很多下限/上限(翻译限制),并规定了每个翻译都应该满足的实现。为什么没有为数组大小定义这样的最小限制?以下程序将编译良好,可能会产生运行时错误/段错误,并会调用未定义的行为

int main()
{
   int a[99999999];
   int i;

   for(i=0;i<99999999;i++)
   a[i]=i;

   return 0;
}

一个可能的原因可能是在自动存储上分配了本地数组,这取决于分配的堆栈帧的大小。但是为什么不像 C 定义的其他限制那样设置最小限制呢?

让我们忘记上面的未定义情况。考虑以下:

int main()
{
   int a[10];
   int i;

   for(i=0;i<10;i++)
   a[i]=i;

   return 0;
}

在上面,是什么让我保证本地数组(尽管非常小)将按预期工作并且不会由于分配失败而导致未定义的行为?

尽管在任何现代系统上分配如此小的阵列都不太可能失败。但是 C 标准没有定义任何要满足的要求,编译器也没有(至少 GCC 没有)报告分配失败。只有运行时错误/未定义的行为是可能的。困难的部分是没有人可以判断任意大小的数组是否会由于分配失败而导致未定义的行为。

请注意,我知道我可以为此目的使用动态数组(通过malloc和朋友),并且可以更好地控制分配失败。我更感兴趣的是为什么没有为本地数组定义这样的限制。此外,全局数组将存储在静态存储中,并将增加编译器可以处理的可执行文件大小。

4

3 回答 3

9

您已经回答了自己的问题;这是由于堆栈限制。* 即使这样也可能行不通:

void foo(void) {
    int a;

    ...
}

如果这...实际上是对 . 的递归调用foo

换句话说,这与数组无关,因为同样的问题会影响所有局部变量。该标准无法强制执行要求,因为在实践中这将转化为对无限大小堆栈的要求。


* 是的,我知道 C 标准不谈论堆栈。但这是隐含的模型,从某种意义上说,标准实际上是当时存在的实现的形式化。

于 2013-02-04T20:43:52.367 回答
9

因为 C语言不应该对您的可用堆栈大小施加限制。C 在许多(许多)不同的环境中运行。它怎么可能得出一个合理的数字?地狱,自动存储持续时间!=堆栈,堆栈是一个实现细节。C,语言,没有说“堆栈”。

环境决定了这些东西,这是有充分理由的。如果某个环境通过没有这种限制的替代方法实现自动存储持续时间怎么办?如果硬件出现突破,突然现代机器不需要这样的限制怎么办?

在这种情况下我们应该修改标准吗?如果 C语言指定了这样的实现细节,我们将不得不这样做。

于 2013-02-04T20:46:10.257 回答
2

MINIMUM 限制是 1 个元素的数组。为什么你会有一个“限制”呢?当然,如果你永远递归调用一个函数,一个 1 的数组可能不适合堆栈,或者调用函数的调用下一次调用可能不适合堆栈 - 解决这个问题的唯一方法是知道编译器中堆栈的大小 - 但在那个阶段编译器实际上并不知道堆栈有多大 - 别介意极其复杂的调用层次结构的问题是几个不同的函数调用同一个函数,可能使用递归和/或几层相当大的堆栈消费者-您如何为此调整堆栈的大小-可能永远不会遇到最坏的情况,因为其他事情表明这种情况不会发生-例如,一个函数中最坏的情况是仅当输入文件为空时,但另一个函数中最坏的情况是同一文件中存储了大量数据。很多很多这样的变化。这太不可靠了,所以迟早会变成猜测或大量误报。

考虑一个具有数千个函数的程序,所有这些函数都调用相同的日志记录函数,该函数需要堆栈上的 200 字节数组来临时存储日志输出。从 main 向上的几乎每个函数都调用它。

局部变量的 MAXIMUM 取决于堆栈的大小,正如我上面所说,编译器在编译代码时并不知道[链接器可能知道,但稍后再说]。对于全局数组和在堆上分配的数组,限制是“您的进程可以获得多少内存”,因此那里没有上限。

没有简单的方法来确定这一点。该标准提供的许多限制是为了保证代码可以在“任何编译器”上编译,只要您的代码遵循规则。被编译和能够运行完成是两件不同的事情。

int main() { while(1); }

永远不会完成 - 但它会在我知道的每个编译器中编译,而且大多数人不会说存在无限循环 - 这是你的选择。

将大型数组放入堆栈也是您的选择。很可能链接器被赋予了几千兆字节的堆栈,在这种情况下它会很好 - 或者堆栈是 200K,你不能有 50000 个整数数组......

于 2013-02-04T20:54:54.780 回答