C编译器可以重新排列堆栈变量吗?

我曾经在嵌入式系统的项目上工作,我们重新安排了堆栈变量声明的顺序,以减少生成的可执行文件的大小。 例如,如果我们有:

void func() { char c; int i; short s; ... } 

我们会将此重新排序为:

 void func() { int i; short s; char c; ... } 

由于对齐问题,第一个导致使用12个字节的堆栈空间,第二个导致仅8个字节。

这是C编译器的标准行为,还是我们使用的编译器的缺点?

在我看来,编译器应该能够重新排序堆栈变量,以便在需要时支持更小的可执行文件大小。 有人向我建议,C标准的某些方面可以防止这种情况,但我无法以任何方式找到信誉良好的来源。

作为一个额外的问题,这也适用于C ++编译器吗?

编辑

如果答案是肯定的,那么C / C ++编译器可以重新排列堆栈变量,你能给出一个肯定会这样做的编译器的例子吗? 我想看看编译器文档或类似的东西支持这一点。

再次编辑

谢谢大家的帮助。 对于文档,我能够找到的最好的东西是GCC (pdf)中的最佳堆栈槽分配 ,由Naveen Sharma和Sanjiv Kumar Gupta撰写,于2003年在海湾合作委员会峰会会议上提交。

这里讨论的项目是使用ADS编译器进行ARM开发。 在该编译器的文档中提到,像我所示的排序声明可以提高性能以及堆栈大小,因为ARM-Thumb架构如何计算本地堆栈帧中的地址。 该编译器没有自动重新排列本地人以利用这一点。 这里链接的文章说,截至2003年,GCC也没有重新安排堆栈帧以改善ARM-Thumb处理器的参考局部性,但它暗示你可以。

我找不到任何明确说明在GCC中实施的内容,但我认为这篇论文可以certificate你是正确的。 再次感谢。

由于标准中没有任何内容禁止C或C ++编译器,是的,编译器可以做到这一点。

聚合(即结构)不同,其中必须保持相对顺序,但编译器仍可插入填充字节以实现优选的对齐。

IIRC较新的MSVC编译器利用这种自由来对抗当地人的缓冲区溢出。

作为旁注,在C ++中,即使编译器重新排序内存布局,破坏的顺序也必须是声明的反向顺序。

(我不能引用章节和诗句,但这是来自记忆。)

编译器不仅可以重新排序局部变量的堆栈布局,还可以将它们分配给寄存器,有时将它们分配到寄存器中,有时在堆栈中,它可以将两个本地分配给内存中的同一个插槽(如果它们的有效范围不重叠)它甚至可以完全消除变量。

堆栈甚至不需要存在(实际上,C99标准没有单个“堆栈”出现)。 所以是的,编译器可以自由地做任何想做的事情,只要保留具有自动存储持续时间的变量的语义。

至于一个例子:我遇到过多次无法在调试器中显示局部变量的情况,因为它存储在寄存器中。

编译器甚至可以自由地从堆栈中删除变量,只有在分析显示变量的地址从未被使用/使用时才使其成为寄存器。

编译器甚至可能根本不使用堆栈来处理数据。 如果你在一个如此小的平台上,你担心8比12的字节堆栈,那么很可能会有编译器具有非常专业的方法。 (一些PIC和8051编译器浮现在脑海中)

你在编译什么处理器?

Texas Instruments 62xx系列DSP的编译器能够实现“整个程序优化”。 (你可以把它关掉)

这是您的代码重新排列的地方,而不仅仅是本地人。 因此,执行顺序最终并不是您所期望的。

C和C ++ 实际上并没有承诺一个内存模型(在JVM的意义上),所以事情可能完全不同而且仍然合法。

对于那些不了解它们的人,62xx系列每个时钟周期DSP有8个指令; 在750Mhz时,它们以6e + 9指令达到峰值。 反正有些时候。 它们执行并行执行,但是指令排序是在编译器中完成的,而不是像Intel x86那样的CPU。

PIC和Rabbit嵌入式主板没有堆栈,除非你特别要求。

它是编译器细节,如果他想要那样的话,就可以制作他自己的编译器。

如果可以,一个体面的编译器会将局部变量放入寄存器中。 如果注册压力过大(空间不够)或变量的地址被占用,变量只应放在堆栈上,这意味着它需要存在于内存中。

据我所知,没有什么可以说变量需要放在C / C ++的堆栈上的任何特定位置或对齐位置; 编译器会将它们放在最适合性能的地方和/或任何方便编译器编写者的地方。

AFAIK在C或C ++的定义中没有任何内容指定编译器应如何在堆栈上对局部变量进行排序。 我会说依赖于编译器在这种情况下可能会做的事情是一个坏主意,因为编译器的下一个版本可能会采用不同的方式。 如果您花费时间和精力来命令本地变量来保存几个字节的堆栈,那么这几个字节最好对系统的运行至关重要。

无需对C标准要求或不要求的内容进行闲置推测:最近的草案可以在ANSI / ISO工作组免费在线获取。

这不能回答你的问题,但这是关于相关问题的2美分……

我没有堆栈空间优化的问题,但我遇到了堆栈上双变量错误对齐的问题。 可以从任何其他函数调用函数,并且堆栈指针值可以具有任何未对齐的值。 所以我想出了下面的想法。 这不是原始代码,我只是写了…

 #pragma pack(push, 16) typedef struct _S_speedy_struct{ double fval[4]; int64 lval[4]; int32 ival[8]; }S_speedy_struct; #pragma pack(pop) int function(...) { int i, t, rv; S_speedy_struct *ptr; char buff[112]; // sizeof(struct) + alignment // ugly , I know , but it works... t = (int)buff; t += 15; // alignment - 1 t &= -16; // alignment ptr = (S_speedy_struct *)t; // speedy code goes on... }