在堆栈中创建大内存

时间:2014-02-27 22:25:59

标签: c gcc compiler-construction

我试图在堆栈中创建大量内存(int a[1000000000])。 成功编译。但是在运行时它是Segfaulted。它非常适合程序段错误。我的问题是,为什么编译器无法检测到此并抛出编译错误?我们为什么要等到程序运行?

此外,当我分配int a[100]时,asm代码如下所示。

00000000004004b4 <main>:
  4004b4:       55                      push   %rbp
  4004b5:       48 89 e5                mov    %rsp,%rbp
  4004b8:       48 81 ec 18 01 00 00    sub    $0x118,%rsp (memory created for 100 bytes)

如果我为大内存int a[1000000000]创建,则asm代码看起来不同。

00000000004004b4 <main>:
  4004b4:       55                      push   %rbp
  4004b5:       48 89 e5                mov    %rsp,%rbp
  4004b8:       49 bb 78 00 17 be 33    movabs $0xfffe9433be170078,%r11
  4004bf:       94 fe ff 
  4004c2:       4c 01 dc                add    %r11,%rsp

有人可以解释一下吗?那编译器为什么不处理这个呢?

由于

CHID

4 个答案:

答案 0 :(得分:9)

  

为什么编译器不能事先检测到这种情况并产生编译错误或警告?

该问题假定前提不正确。很少有技术上阻止编译器编写者这样做。你可能希望它是一个警告,而不是一个错误,但编译器编写者可以轻松地为一个函数使用的本地存储总量的不合理的巨大大小设置一个阈值,如果该大小是超出。

由于您的问题是基于错误的前提,因此无法回答。问一个更好的问题。

  

好的,如果可能那么为什么编译器编写者实现此警告?

无论您的编译器是开源还是闭源,无论是开发者还是志愿者,所有对软件的更改都需要时间努力。两者都供不应求。

编译器警告是功能,因此必须进行设计,指定,实施,测试,调试,发运和维护。所有这些事情在时间和精力上都很昂贵,并且所有这些都其他功能中删除时间和精力。

如果您喜欢的编译器中不存在此功能,那是因为该编译器的开发人员有大约一百万个更好的事情要做。在这一百万件更好的事情中,今年他们可能会设法实施几十件。如果你想提倡你的特定功能建议将列表从当前的低位置上移,请开始调用编译器开发人员并向他们说明这是他们花费精力的最佳方式 - 为明显虚假且立即崩溃的情况创建警告。你可能不会走得太远。

在考虑要实现哪些编译器警告时,像我这样的编译器编写者会认真思考要添加到编译器的警告。良好警告可识别以下情况:

  • <强>法律;非法代码当然是错误的。
  • 可检测,误报率低;指示开发人员将正确的工作代码更改为损坏代码的警告是一个糟糕的警告
  • 误导性;程序员应该相信代码做了一件事实际上它做了另一件事
  • 似是而非;程序员应该合理地在业务线程序中键入警告代码
  • 通过琐碎的测试无法检测;程序应该正常运行并做一些巧妙的错误。如果程序立即崩溃,那么警告只是在测试发现之前几秒钟发现错误,这不是很好的价值。

您的提案符合前两个标准。它可以说满足第三个。它毫不含糊地无法满足第四和第五个标准。因此,如果我是编译器开发人员,我会拒绝你的建议。我会花费宝贵的时间和精力来添加更有利于成本的功能。

答案 1 :(得分:4)

编译器不负责堆栈分配。二进制文件的堆栈空间由链接器保留。例如,ld--stack选项:

  

- 筹码储备

     

- 堆栈保留,提交

   Specify the number of bytes of memory to reserve (and optionally commit)
   to be used as stack for this program.  The default is 2Mb reserved,
   4K committed.  [This option is specific to the i386 PE targeted
   port of the linker]

此外,该进程可以克隆自身以创建其他进程/线程。那时,创建克隆的人控制为堆栈分配多少内存,编译器和链接器都不能帮助那里。

最后,操作系统可能还有其他限制。例如,Linux对堆栈大小有软硬限制,并且如果它违反了为进程设置的限制,则不允许任何进程创建其堆栈。请参阅ulimit

理论上,编译器可以分析程序并尝试估计总堆栈使用情况,并且,例如,提示链接器应该尝试为堆栈保留多少。然而,如果例如使用动态链接,则这变得非常成问题。或者,例如,程序具有递归,其深度取决于运行时变量(即,一些图搜索算法)。然后没有办法做到这一点。鉴于在编译器中实现这样的启发式算法远非微不足道,而且大多数程序都有某种递归或动态链接这一事实,这根本不值得付出努力。

答案 2 :(得分:3)

编译器无法识别所有目标机器限制

编译器任务只是用您的CPU /架构的机器码等效形式编译代码

你有没有见过编译器抱怨堆中分配了太多内存? : - )

编译器转码低级机器代码等效的高级程序

答案 3 :(得分:1)

这是为了使它在具有相同拱形但具有不同规格的机器之间更加兼容。

因为编译器无法预测机器的规格,所以你将运行二进制文件。运行二进制文件时,它必须是类似的arch(x86 / ARM),但具有相同体系结构的计算机可以具有不同的内存规范。