为什么C没有为数组定义最小大小?

时间:2013-02-04 20:41:38

标签: c arrays undefined-behavior

C标准定义了许多下限/上限( translation limits ),并强制实施应满足每个翻译。为什么没有为数组大小定义这样的最小限制?以下程序将编译正常并可能产生运行时错误/段错误,并将调用未定义的行为

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 &amp; friends)来实现此目的,并可以更好地控制分配失败。我更感兴趣的是为什么没有为本地数组定义这样的限制。此外,全局数组将存储在静态存储中,并将增加编译器可以处理的可执行文件大小。

3 个答案:

答案 0 :(得分:9)

你已经回答了自己的问题;这是由于堆栈限制。 * 即使这可能也不起作用:

void foo(void) {
    int a;

    ...
}

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

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

<小时/> *是的,我知道C标准没有谈论堆栈。但这就是隐式模型,因为标准实际上是当时存在的实现的形式化。

答案 1 :(得分:9)

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

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

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

答案 2 :(得分:2)

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

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

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

确定这一点并不容易。并且标准提供的许多限制是为了保证只要您的代码遵循规则,就可以在“任何编译器”上编译代码。编译并能够完成运行是两回事。

int main() {     而(1); }

将永远不会运行完成 - 但它会在我所知道的每个编译器中编译,并且大多数都不会说有关于存在无限循环的事情 - 这是你的选择。

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