C标准对使用的堆栈空间量有任何保证吗?

12

我正在从事嵌入式编程,其中节省内存非常重要。

以下 C 代码在运行时将占用多少堆栈空间?

if (send_small_message) {
    uint8_t buffer[16000];
    // do something with the buffer
} else {
    uint8_t buffer[32000];
    // do something the with buffer
}

某些编译器是否可能决定为两个缓冲区分配16000+32000 = 48kB的堆栈空间?还是可以保证,由于两个缓冲区永远不会同时使用,编译器将仅分配32kB - 更大的缓冲区的大小?

跟进问题:

void SendSmallMessage() {
    uint8_t buffer[16000];
    // do something with the buffer
}

void SendLargeMessage() {
    uint8_t buffer[32000];
    // do something with the buffer
}

某个编译器编译的代码能否在运行时使用16000 + 32000字节来执行以下代码片段:

if (send_small_message) {
   SendSmallMessage(); 
} else {
   SendLargeMessage();
}

8
C标准并没有强制使用“栈”。根据编译器的质量、编译选项等,你的代码片段最终可能会使用32或48k内存(栈或其他)。 - pmg
实际上,如果您应用优化标志,这永远不会超过32000。但我仍然会使用宏来解决这种情况,因为您可能会为不同的设备重新编译代码,这样您就可以得到保证的结果。 - user1502256
1
@para,您如何使用宏来解决这个问题?在我的情况下,同一设备可以根据控制流使用较小或较大的缓冲区。 - mercury0114
2
请使用合理的程序设计来解决这个问题,而不是使用宏。 - Lundin
1
你可以检查编译器的汇编输出,这将向你展示确切发生了什么。 - Jabberwocky
你无法在目标上测试吗?也许可以添加一些缓冲区大小的简单printf。 - 2785528
2个回答

11

C标准是否保证了使用的堆栈量?

完全没有任何保证。C标准未提及堆栈等概念。你甚至可以为完全缺乏堆栈的低级CPU编写C代码。

然而,C标准确保uint8_t占用1字节,并且在您的系统上1字节等于8位(否则uint8_t将不可用)。

以下C代码在运行时将占用多少堆栈空间?
某些编译器是否会决定为这两个缓冲区分配16000 + 32000 = 48kB的堆栈空间?

具体取决于系统,并且还取决于函数写法和所执行的优化。但通常情况下,真实的系统会根据所有可能的执行路径分配函数所需的堆栈空间。因此,许多编译器很可能会分配16k + 32k的空间。

但是,没人会关心这个问题,因为在任何已知的系统中,都没有意义在堆栈上分配那么大的内存。高端类似PC的系统也不行,受限于内存的嵌入式系统则更加不能承受。这样会导致堆栈溢出问题。

在嵌入式领域,通常的经验法则是永远不要在堆栈上分配任何形式的缓冲区,而是始终使用静态存储期。在类似PC的系统中,另一个选择是堆分配。


我认为我会用“可能有一些编译器”来代替“很可能有许多编译器”,虽然我很幸运到目前为止还没有遇到过这样的编译器! - Ian Abbott
现代PC编译器肯定会进行优化。但是如果你在过去几十年中使用过各种更或少有缺陷的嵌入式系统编译器,你会发现很多编译器几乎不能进行任何优化。 - Lundin
@Lundin:即使在嵌入式系统中,这也是有意义的。 "优势" 可能是自动存储期限,您只需使用提供的堆栈,而不是使用多个静态缓冲区仅用于一次,或者使用某种联合来共享它们。我们的汽车ADAS ECU需要计算大量数据,例如成千上万的雷达检测,以构建各种功能的环境模型(ACC、EBA、BSD/LCA..自动驾驶)。在我们的ShortRangeRadar的情况下,我们有一个DSP + R5F和3.5MB的代码和数据,每个字节都很重要。 - kesselhaus
C标准确保uint8_t是1字节大小。你确定吗?我在某个地方读到说这些类型是可选的,但最小和最快速度的宽度类型是必需的。https://dev59.com/3nA75IYBdhLWcg3wm6ax#_J2cEYcBWogLw_1byOaA - pratikpc
是的,它是可选的,因此如果一个系统没有8位字节,则不会有 uint8_t。只有具有与8不同的字节大小的奇特、过时的 DSP 才不具备它。 - Lundin

5

@Lundin提供了优秀的答案。但我想从略微不同的角度回答。

C标准基本上保证了代码的行为,但它并没有保证代码执行的方式。它有可能(我不知道多大概率)会将缓冲区声明移到if语句之外。它也可以分配比指定内存更多的内存,如果这样做不会出问题,也可以分配比指定内存更少的内存。通常,未使用的变量会被优化器删除。优化器还经常将小函数内联而不是调用它们,并且它可能会将printf("\n")更改为puts("")。只要代码的可观察行为保持不变,编译器就可以随意做任何事情。

因此,在这种情况下,你没有任何保证。

但有一件事要考虑。你想根据if语句声明不同大小的缓冲区。假设这16kB的额外空间会导致栈溢出。如果剩余的栈空间少于32kB并且需要执行else分支,你该怎么办?当然,这取决于代码在现实中的使用方式,但这绝对值得考虑。对我来说,这是一个相当严重的代码味道。


或者我可以这样说(有点过于简洁):“语言和编译器无法解决设计问题。” - andy mango

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