正如其他博客所提到的,局部变量的生命周期是在一个大括号内,即一个所处块结束。
局部变量和全局变量的区别,局部变量的生命周期是从创建开始到所处的块结束就被回收,而全局变量的生命周期是从创建开始到程序结束。
正如上面所说,局部变量是直到所处的块结束才从调用栈中把它回收。先看下面的代码。
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
既然临时变量会在每一次循环回收,那为什么有的时候还能得到正确的值呢?这是以前第二次循环为变量分配的地址与原来的地址相同,将变量内存释放时也没进行内存擦除操作,故打印结果为第一次所赋的值。