avr-gcc 程序中的 main() 函数将寄存器状态保存在堆栈上,但是当运行时调用它时,我知道在微控制器上没有任何东西可以返回。这是浪费内存吗?如何防止这种状态保存?
5 回答
编译器如何确定您不会递归调用 main()?
这都是关于 C 标准的。
没有什么能阻止你在某个时候退出 main。您可能不会在您的程序中这样做,但其他人可能会这样做。
atexit
此外,您可以通过运行时函数注册清理处理程序。这些函数需要一个定义好的寄存器状态才能正确执行,而保证这一点的唯一方法是保存和恢复 main 周围的寄存器。
这样做甚至可能很有用:我不了解 AVR,但其他微控制器在完成工作并等待重置时会进入低功耗状态。从清理处理程序中执行此操作可能是一个好主意,因为如果您以正常方式退出 main并且(就我现在而言)如果您的程序通过终止信号中断,则会调用此处理程序。
很可能 main 只是编译为与标准函数相同。在 C 中,它几乎需要是因为您可能会从某个地方调用它。
请注意,在 C++ 中,递归调用 main 是非法的,因此 C++ 编译器可能能够对此进行更多优化。但是在 C 中,正如您的问题所述,递归调用 main 是合法的(如果是个坏主意),因此它需要以与任何其他函数相同的方式进行编译。
如何防止这种状态保存?
您唯一能做的就是编写自己的 C-Startup 例程。这意味着要弄乱汇编程序,但是您可以然后跳转到您的 main() 而不仅仅是调用它。
在我使用 avr-gcc 4.3.5 进行的测试中,如果没有进行太多优化,它只会节省寄存器。正常级别(-Os 或 -O2)会导致推送指令被优化掉。可以在函数声明中进一步指定它不会以__attribute__((noreturn))
. 使用 -fwhole-program 进行完整的程序优化也很有用。
avr-libc 中的初始代码确实使用 call 跳转到 main,因为它指定 main可以返回,然后跳转到 exit(声明为 noreturn,因此不生成调用)。如果您认为这太多了,您可以链接您自己的变体。exit() 反过来只是简单地禁用中断并进入无限循环,有效地停止您的程序,但不会节省任何电力。如果您的 main() 从不返回或调用 exit(),那将是四个指令和两个字节的堆栈内存开销。