[疑难问答]

单片机堆栈说明与全局变量和局部变量的存储位置

[复制链接]
3374|53
手机看帖
扫描二维码
随时随地手机跟帖
wilhelmina2|  楼主 | 2023-9-16 22:00 | 显示全部楼层 |阅读模式


事实上堆(heap)和栈(stack)是两种不同的数据结构,我不太明白为什么国内的教材要称呼其为堆栈,因此以下的内容都称其为栈。我们可以尝试通过一个简单的函数调用来看看局部变量是如何在汇编代码中存储的。以下的结果是我在M1 Macbook Pro上用x86_64-elf-gcc编译得到的结果。C代码如下int add(int x, int y){    int z = 1;    return x + y + z;}int main(){    add(1, 2);    return 0;}
编译语句为
x86_64-elf-gcc -O0 -o main.s -S main.c
生成的汇编代码我选取了add函数并去掉了无关的注释,如下
add:        pushq        %rbp        movq        %rsp, %rbp        movl        %edi, -4(%rbp)        movl        %esi, -8(%rbp)        movl        -4(%rbp), %edx        movl        -8(%rbp), %eax        addl        %edx, %eax        popq        %rbp        ret
创建局部变量的过程在于movl %edi, -4(%rbp) 和movl %esi, -8(%rbp)。在前两步中函数保存了并更新rbp值 ,而传递的参数初始保存在edi和esi寄存器中。因此前两部执行完后栈分布情况大致如下----------------------------------------the value of previous rbp               <- rbp----------------------------------------
接下来通过两条movl指令,函数向相对rbp的指定偏移地址写入参数。注意栈是自顶向下增长的。情况如下 ----------------------------------------the value of previous rbp               <- rbp----------------------------------------argument0----------------------------------------argument1----------------------------------------
在这之后传入的变量就被存储在栈上了,也就是说我们可以通过相对rbp的偏移量去访问到这两个参数。例如,在接下来的加法运算中,函数首先就通过两条加法指令将数据从对应的偏移量地址读取到寄存器中,再执行加法操作。最后,当函数执行完成时,函数会进行退栈。注意到最后一步 popq %rbp ,事实上是恢复了原始的rbp值,这意味着在函数返回之后我们无法再通过原先相对rbp的偏移量再去访问到原先局部变量的值,函数调用栈事实上被销毁了。当然,在更复杂的例子中rsp指针也应当被移动,并在返回时被重新对齐。可能是我们的C函数过于简单,尽管关闭了优化,编译器依然没有移动它。 如果是在函数中自己创建局部变量,道理也同传入参数是一样的,始终是将数据存储在栈上再通过相对rbp或者rsp的偏移量去找到相应的数据。

使用特权

评论回复
tpgf| | 2023-10-11 12:11 | 显示全部楼层
楼主是不是考虑重新对文章内容重新排版啊

使用特权

评论回复
qcliu| | 2023-10-11 13:26 | 显示全部楼层
能否将汇编代码的部分加一下相关的注释呢

使用特权

评论回复
drer| | 2023-10-11 15:47 | 显示全部楼层
这样操作的话 如果数据溢出了怎么办呢

使用特权

评论回复
coshi| | 2023-10-11 16:32 | 显示全部楼层
如何防止误操作修改相应地址的数据呢

使用特权

评论回复
kxsi| | 2023-10-11 16:49 | 显示全部楼层
退栈之后再次存储其他数据怎么知道存在哪个地址上了呢

使用特权

评论回复
wiba| | 2023-10-11 17:12 | 显示全部楼层
堆和栈就必须要存储对应的数据 不能混淆使用是吗

使用特权

评论回复
vivilyly| | 2023-10-17 10:07 | 显示全部楼层
单片机堆栈是一种特殊的内存结构,用于临时存储数据和返回地址。堆栈的工作原理是“后进先出”(LIFO),即最后入栈的数据最先出栈。在单片机中,堆栈通常位于内部RAM的最高地址。

使用特权

评论回复
uiint| | 2023-10-17 10:41 | 显示全部楼层
全局变量是定义在程序全局作用域中的变量,其生命周期通常贯穿于整个程序运行期间。这些变量在内存中的静态存储区进行分配。

使用特权

评论回复
pl202| | 2023-10-17 10:51 | 显示全部楼层
函数被调用时,堆栈指针会向下移动,为函数的局部变量和参数分配内存空间。当函数返回时,堆栈指针会恢复到原始位置,释放函数占用的内存空间。

使用特权

评论回复
claretttt| | 2023-10-17 11:23 | 显示全部楼层
堆栈的大小通常是固定的,因此函数调用过程中的局部变量和参数数量可能会受到堆栈大小的限制。

使用特权

评论回复
iyoum| | 2023-10-17 12:37 | 显示全部楼层
在多个函数之间共享某些数据的情况,可以考虑使用全局变量。

使用特权

评论回复
pentruman| | 2023-10-17 12:57 | 显示全部楼层
单片机堆栈在函数调用、中断处理和返回语句等场景中起着重要的作用,它用于存储临时数据和返回地址。

使用特权

评论回复
rosemoore| | 2023-10-17 13:06 | 显示全部楼层
在单片机中,堆栈是一种数据存储结构,用于存储程序运行时所需的数据和指令。堆栈的存储位置是在程序运行时动态分配和释放的,通常由CPU的栈指针控制。

使用特权

评论回复
ulystronglll| | 2023-10-17 13:22 | 显示全部楼层
局部变量的生命周期只限于函数调用期间,而全局变量的生命周期则贯穿整个程序执行过程。

使用特权

评论回复
i1mcu| | 2023-10-17 13:30 | 显示全部楼层
当一个函数被调用时,单片机会将该函数的返回地址、函数名和参数等数据压入堆栈,然后执行该函数。在函数内部,如果使用了局部变量,那么这些局部变量也会被压入堆栈。当函数执行完毕后,单片机会从堆栈中依次弹出函数的返回地址、函数名和局部变量等数据,然后执行返回语句

使用特权

评论回复
lzbf| | 2023-10-17 13:42 | 显示全部楼层
全局变量是静态存储在内存中的,可以在程序的任何地方访问。局部变量是存储在栈中的,它们只在函数内部有效,当函数返回时,局部变量被自动清除。

使用特权

评论回复
uytyu| | 2023-10-17 14:15 | 显示全部楼层
全局变量是在整个程序中都可见可用的变量,它们的生命周期从声明处开始,直到程序运行结束。

使用特权

评论回复
nomomy| | 2023-10-17 14:40 | 显示全部楼层
局部变量的生命周期和可见性是有限的,只能在函数或代码块内部使用。

使用特权

评论回复
jonas222| | 2023-10-17 15:10 | 显示全部楼层
如果堆栈太小,可能会导致函数调用失败或程序崩溃。因此,在编写单片机程序时,需要合理分配堆栈大小,以满足程序的需求。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

19

主题

1223

帖子

1

粉丝