循环内部局部变量与调用栈

本文探讨了局部变量的生命周期,尤其是循环内部变量如何在调用栈中运作。局部变量在所属代码块结束后被回收,而全局变量在整个程序执行期间存在。调用栈用于存放返回地址、本地变量和参数传递。通过将for循环转换为while循环,我们可以更好地理解循环内部变量只有一轮生命周期的原因。此外,文章提到了汇编级别的循环实现,并指出在某些情况下,循环变量能保留正确值是因为内存未被擦除,导致两次循环间地址重用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

正如其他博客所提到的,局部变量的生命周期是在一个大括号内,即一个所处块结束。

局部变量和全局变量的区别,局部变量的生命周期是从创建开始到所处的块结束就被回收,而全局变量的生命周期是从创建开始到程序结束。

正如上面所说,局部变量是直到所处的块结束才从调用栈中把它回收。先看下面的代码。
for(int i = 0;i<10;i++)
{
int j=0;
}

上面代码中的的整型变量i知道for循环结束之后才被调用栈回收。它经历了10次for的运算,知道i=10的时候才退出循环,i被调用栈销毁。而j在每一次循环都完成一次入栈出栈的操作。

那么调用栈是什么,它又是如何怎么运作的呢?

调用栈(Call stack,英文直接简称为“栈”(the stack))最经常被用于存放子程序的返回地址。在调用任何子程序时,主程序都必须暂存子程序运行完毕后应该返回到的地址。因此,如果被调用的子程序还要调用其他的子程序,其自身的返回地址就必须存入调用栈,在其自身运行完毕后再行取回。在递归程序中,每一层次递归都必须在调用栈上增加一条地址,因此如果程序出现无限递归(或仅仅是过多的递归层次),调用栈就会产生栈溢出。

调用栈的主要功能是存放返回地址。除此之外,调用栈还用于存放:
本地变量:子程序的变量可以存入调用栈,这样可以达到不同子程序间变量分离开的作用。
参数传递:如果寄存器不足以容纳子程序的参数,可以在调用栈上存入参数。
环境传递:有些语言(如Pascal与Ada)支持“多层子程序”,即子程序中可以利用主程序的本地变量。这些变量可以通过调用栈传入子程序。

光是了解调用栈还不足以理解循环内部定义的变量为什么只有一次循环的生命周期。

关于循环到底是如何在汇编级别实现的,请转向这个《深入了解计算机系统》的博客,进一步了解循环,从而理解为什么循环内部定义的变量的生命周期只有一个循环。
https://wdxtub.com/2016/04/16/thin-csapp-2/

简单的来说,就是先把for转化为while

// For
for (Init; Test; Update)
Body

// While Version
Init;
while (Test) {
Body
Update;
}

然后以一个do-while的代码为例子来看它的汇编。
// Do While 的 C 语言代码

long pcount_do(unsigned long x)
{
    long result = 0;
    do {
        result += x & 0x1;
        x >>= 1;
    } while (x);
    return result;
}

这个函数计算参数 x 中有多少位是 1,翻译成汇编如下:

movl    $0, %eax    # result = 0
.L2:                    # loop:
movq    %rdi, %rdx
andl    $1, %edx    # t = x & 0x1
addq    %rdx, %rax  # result += t
shrq    %rdi        # x >>= 1
jne     .L2         # if (x) goto loop
rep; ret

既然临时变量会在每一次循环回收,那为什么有的时候还能得到正确的值呢?这是以前第二次循环为变量分配的地址与原来的地址相同,将变量内存释放时也没进行内存擦除操作,故打印结果为第一次所赋的值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值