关于网友提出的“ 我今天还非弄明白不可了 关于栈 变量作用域 生命周期 内存管理”问题疑问,本网通过在网上对“ 我今天还非弄明白不可了 关于栈 变量作用域 生命周期 内存管理”有关的相关答案进行了整理,供用户进行参考,详细问题解答如下:
问题: 我今天还非弄明白不可了 关于栈 变量作用域 生命周期 内存管理
描述: 我还不信了 关于这个问题 这是第三个帖子了 但至今还没出现过大虾 今天一定要弄明白
void fun()
{
int a = 0;
{
int b = 0;
int c = 0;
}
int d = 0;
}
这是第一段代码 编译器是vs2008 算是比较新的了 下面是反汇编出来的代码:
void fun()
{
00401000 push ebp
00401001 mov ebp,esp
00401003 sub esp,0F0h
00401009 push ebx
0040100A push esi
0040100B push edi
0040100C lea edi,[ebp-0F0h]
00401012 mov ecx,3Ch
00401017 mov eax,0CCCCCCCCh
0040101C rep stos dword ptr es:[edi]
int a = 0;
0040101E mov dword ptr [a],0
{
int b = 0;
00401025 mov dword ptr [b],0
int c = 0;
0040102C mov dword ptr [c],0
}
int d = 0;
00401033 mov dword ptr [d],0
}
0040103A pop edi
0040103B pop esi
0040103C pop ebx
0040103D mov esp,ebp
0040103F pop ebp
00401040 ret
哪位汇编狠的大虾能解释一下 这段汇编代码是嘛意思呀 看不懂呀看不懂
其实我就是想知道 当fun函数被调用的时候 函数中的局部变量 是一下子全都被分配了内存空间 还是先分配了a和d的内存空间 等到运行到第二个{的时候 b和c才被分配了内存空间呢
还想知道在释放abcd的内存的时候 是走到函数的最后 abcd一起都释放内存空间 还是走到第一个}的时候先释放b和c 然后走到第二个}的时候再释放a和d 还是走到第二个}的时候 abcd一起释放内存空间呢?????
再来一段for循环的:
void fun()
{
for(;;)
{
int a = 0;
}
}
以下是汇编代码:
void fun()
{
00401000 push ebp
00401001 mov ebp,esp
00401003 sub esp,0CCh
00401009 push ebx
0040100A push esi
0040100B push edi
0040100C lea edi,[ebp-0CCh]
00401012 mov ecx,33h
00401017 mov eax,0CCCCCCCCh
0040101C rep stos dword ptr es:[edi]
for(;;)
{
int a = 0;
0040101E mov dword ptr [a],0
00401025 jmp fun+1Eh (40101Eh)
}
}
大虾再帮忙解释一下这段汇编代码呗
其实我还是想知道 变量a是fun刚被调用的时候就被分配空间了 还是进入到for的时候才被分配空间 释放的时候 是每次循环走出for的}的时候 a的内存空间就被释放了 还是等函数走完之后 a才会被释放????
如果是函数走完之后 a才会被释放 那么在for中 每次int a = 0; 不是每次都重新定义了一个a吗 既然是定义 就要被分配内存空间 那么 起不是每次循环都要分配新的a的内存空间了啊????
分不多了 我都用上了 大虾们帮忙帮忙
解决方案1: 之所以你不明白,是因为你把c的概念往汇编里靠:
所谓为变量"分配"内存,实际上只是对一个栈上的空间写入一个值而已,象"int A;"这条指令,c里面这条语句只是告诉编译器你需要一个栈上的int这么长的空间,编译器会自动选择一个合适的某某地址,但实际上这条c++的语句在汇编里什么也不做,是空的。因为栈的空间早就分配好了。直到你什么时候往这个变量写数据比如A=0;,才会产生相应的:
mov 某某地址,0
即将0写入某某地址。
同理的,所谓销毁局部变量,也没有真正意义上的"销毁动作"。
所以说,对于栈上的变量,根本就不存在"分配"空间的问题,对于汇编来说,同一个函数里也不存在作用域的问题,理论上局部变量可以写在栈里的任意地址(前提是该位置没被别的占用),当然c的编译器有他自己的策略。总之,局部变量是否没销毁干净的问题,根本不用考虑。
换了例子,你认识一堆人,每个都住在某公寓的不同房间(内存地址),你要求前台(编译器)按照人名(变量名称)对应房间号。当你对前台提出"向张三的房间送水的服务"时,前台会翻译名字到地址,然后向这个地址送东西。但是,若你只告诉前台:"?我的朋友张三打算入住"(int A),酒店此时会什么也不干,只是把张三的名字和某房间号进行了登记,这个登记有可能发生在任何时候,但仅仅是登记,不提供任何服务。过几天你说:"张三已经不住在某房间了。"(A被销毁),此时前台依然什么也不用干。因为你并没有要求酒店对张三提供服务。前台此时只是知道了张三住的房间现在"应该"已经空了
那么,张三虽然不住了,但房间还在。那么你会担心:"我的朋友太多,房间会不会不够住?"除非你同时(注意是同时)拥有一大大堆朋友(例如double[65536];),那确实会不够用(你的编译器会提示你的)。但酒店对"张三已经不住这里了"这条指令不作任何表示的行为丝毫不会减少房间数。下回李四入住(int B)的时候让李四住原来张三的房间就行了。那么,至于李四是否会入住张三曾经的房间还是新开一间房,完全用不着你来考虑。酒店自己为你安排。除非你对酒店提出不合理要求,或者通过不法手段篡改了登记簿,或者酒店经理是个白痴(有连栈都搞不定的编译器吗。。。)否则房间永远够用,或者它会在不够用的时候告诉你。
最后,当你全部需要的服务都实现了时(函数执行完了),酒店会自动拆掉自己。无论你的朋友们是否还在入住!
所以,综上所述,楼主担心的问题其实不是问题,概念不要混淆。超过生存期的,它确实"销毁"了,但是销毁未必指具体某个cpu的行为。创建的,它肯定创建了,但"创建"未必是一个具体行为。
解决方案2:
实际上内存“回收”并不一定是一个动作。说不定什么也不做,只是编译器意识到之后可以再利用这段内存。
好吧。我就用“回收”来解释一下for{};
int b;//在b的内存地址的下一个可用地址(勉强这么说),就是for里面的{};所要用的储存变量的地址的开始。可用地址变成{}里面最后一个变量的下一个。
for()
{
int a;
};//到这里,{}里面的变量被“回收”,可用地址又变成了b后面的那个,进入下一个for循环,但是又应该为这个for()里面的{};重新“分配”空间,还是从b的下一个地址开始。
解决方案3: 楼主为什么老纠结这个堆上的空间呢,每个编译器有自己的实现,还牵扯到os
其实你只要清楚你的变量的生命周期就够了,至于它所占的空间是否还在
是否被回收了,这些都是编译器的事情了,只要出了作用域,生命周期结束,不管
它的空间是否回收了,你也不能用它了
对于你上面发的vs的代码,我简单给你说一下吧,
vs对于函数内的局部变量,一般都是在进入函数的时候一次分配,退出的时候一次回收
void fun()
{
00401000 push ebp
00401001 mov ebp,esp
00401003 sub esp,0F0h // 为函数内局部变量统一分配栈上空间
00401009 push ebx 你可以尝试在这个函数里定义一个int a[1024]
0040100A push esi 你会看到0F0h 这个值会变得更大
0040100B push edi
0040100C lea edi,[ebp-0F0h]
00401012 mov ecx,3Ch
00401017 mov eax,0CCCCCCCCh
0040101C rep stos dword ptr es:[edi]
int a = 0;
0040101E mov dword ptr [a],0
{
int b = 0;
00401025 mov dword ptr [b],0
int c = 0;
0040102C mov dword ptr [c],0
}
int d = 0;
00401033 mov dword ptr [d],0
}
0040103A pop edi
0040103B pop esi
0040103C pop ebx
0040103D mov esp,ebp 回收栈上的空间
0040103F pop ebp
00401040 ret
但在特别情况下vs会许会特别处理,没标准要求必须统一一次分配
以上介绍了“ 我今天还非弄明白不可了 关于栈 变量作用域 生命周期 内存管理”的问题解答,希望对有需要的网友有所帮助。
本文网址链接:http://www.codes51.com/itwd/3785076.html