原帖 | Jack | 2025-11-08 11:14 | 👍0 | 阅读约1
关于堆栈和__main()在什么时候初始化
我们的栈在一开始复位的时候,MSP 就被中断向量表的 initial_stack 值加载进去了,也就是说硬件直接把向量表[0]里的那个初始栈顶地址写进 MSP 寄存器。到能执行 __main(ARMCC/Armlib)或者 __libc_init_array(GCC/Newlib)的时候,调用栈其实早就已经可以用了,否则根本没办法进行函数调用、保存返回地址这些最基本的操作。
__main 或 C 运行时真正做的事情主要就是:
把 .data 段从 Flash 复制到 RAM;
把 .bss 段清零(ZI 段与堆栈区的包含关系见 ZI_data 不仅包含 未初始化:初始化为0的全局和静态变量);
如果是 C++ 工程,就调用全局构造函数;
最后再去调用用户的 main()。
栈在最开始的时候只是一块已经预留好的内存空间,它的“开辟”是通过链接脚本静态分配完成的(堆栈大小在哪里设置见 Linker Setting中),并不需要显式地去 malloc 或初始化。你如果想做“填充特定值”(比如 0xA5A5A5A5),那是你自己在启动文件里去做的事情,属于调试或检测用途,不是 C 运行时的默认行为。
堆的初始化机制不一样。堆通常只是通过链接脚本确定边界,比如 _end 或 __heap_start 这些符号确定它的起始地址。如果你想预先填充堆内存,也得你自己去写。真正的堆分配器(比如 malloc)在第一次被调用时,才会根据这些边界去建立自己的分配索引表或链表结构,后续的内存申请和释放才有记录可查。
所以整体上:
栈的空间早就准备好了(硬件加载 MSP 完成“启用”),但是否上色是你自己决定的;
堆的边界是预先确定的,但真正的分配机制只有在第一次 malloc 时才开始运作。
相关笔记