自动存储期太大的对象是否属于未定义行为?

12

最近在SO上的一个问题涉及“为什么在特定情况下在堆栈上分配大元素不会失败?”以及一系列其他关于“在堆栈上使用大数组”或“堆栈大小限制”的问题,这促使我搜索标准中记录的相关限制。

我知道C标准没有指定“堆栈”,因此它不定义任何与这种堆栈相关的限制。但我想知道在 void foo() { char anArray[SIZE_X]; ... } 中,标准保证程序工作的SIZE_X是多少,如果程序超出了这个SIZE_X会发生什么。

我找到了以下定义,但我不确定这个定义实际上是否保证了具有自动存储期的对象的特定支持大小(参见这里在线的C11标准草案):

翻译如下:

最近在 SO 上有一个问题涉及“为什么在特定情况下在堆栈上分配大元素不会失败?”以及一系列其他关于“在堆栈上使用大数组”或“堆栈大小限制”的问题,这促使我搜索标准中记录的相关限制。

我知道 C 标准没有指定“堆栈”,因此它不定义这种堆栈的任何限制。但我想知道标准保证程序能够正常工作的SIZE_X在哪个范围内,以及如果程序超出了这个SIZE_X会发生什么。

我找到了以下定义,但我不确定这个定义实际上是否保证了具有自动存储期的对象的特定支持大小(请参阅 此处在线C11标准草案):

5.2.4.1 翻译限制

(1) 实现必须能够翻译和执行至少包含以下每一个限制的一个程序:

...

在主机环境中,一个对象的大小不超过 65535 字节

这是否意味着在类似于void foo() { char anArray[SIZE_X]; ... }的函数中,实现必须支持SIZE_X的值高达65535,而SIZE_X大于65535的任何值都是未定义行为?

对于堆,调用返回NULLmalloc允许我控制请求“太大对象”的尝试。但是如果程序“请求具有自动存储期限的过大对象”,特别是如果这样的最大大小未在limits.h等文件中记录,我如何控制程序的行为?因此,是否可以编写可移植的函数checkLimits(),支持像以下这样的“入口屏障”:

int main() {
   if(! checkLimits()) {
      printf("program execution for sure not supported in this environment.");     
      return 1;
   } else {
      printf("might work. wish you good luck!"); 
   }
   ...
}

3
这不会导致堆栈溢出吗? - zerkms
为什么它不是未定义行为? - Zimano
1
我不知道标准文本需要什么,但是记录一下,我遇到的声称符合C99标准的编译器所需的最小实际空间略小于128字节。 - doynax
1
问题显然是堆栈溢出,但标准甚至没有提到“堆栈”这个词。也许我们应该要求编译器作者使堆栈溢出的程序(如链接问题中的程序)不会崩溃,因为它们是完全定义好的。 - Petr Skocik
1
这样的 checkLimits 方法存在一个问题,即在许多情况下,它会受到可用内存量的限制。在有多个程序运行的环境中,当您调用 checkLimits 和实际调用 foo 之间,其他一些程序可能会分配内存。 - Daniel H
显示剩余6条评论
1个回答

6
从技术角度来说,一种实现只需翻译并执行一个大小为65,535字节(以及其他列出的东西)的目标文件,就能符合规范。但它可能在其他所有程序上运行失败。
要确保大型程序正常工作,您必须依赖于特定实现的详细信息。大多数实现都提供了比64 KiB更多的堆栈空间,尽管这可能未经记录。可能有链接器开关用于调整允许的堆栈空间。
例如,在当前macOS上使用的ld链接器的默认值为8 MiB,可以使用-stack_size开关设置更多或更少(对于主线程)。
我认为,由于C标准指出环境限制(如堆栈空间)可能会限制实现,因此除了一个特定样本程序必须可行之外,任何其他情况技术上都是未定义的行为。

2
大多数实现提供的堆栈空间超过64 KiB,但是由于每年使用C语言的嵌入式处理器数量众多且资源有限,因此我对“大多数”表示怀疑。 - chux - Reinstate Monica
2
@chux:如果你想要技术上的准确性,我认为这些都不是C语言的实现,因为它们没有记录C标准要求实现记录的所有内容。我相信符合C标准的实现数量为零,其中51%具有大堆栈。 - Eric Postpischil
2
完全同意“0的51%”这一部分。 - chux - Reinstate Monica

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