From: http://www.cnblogs.com/wy-wangyan/archive/2013/04/30/3051487.html
问题如下:
最近查找bug时碰到了循环体中局部变量的内存分配相关的问题,于是查找相关知识,产生了一点疑问。先写代码:
1 int main()
2 {
3 int i;
4 bool bl = true;
5 for (i=0; i<3; i++)
6 {
7 int nvar[10];
8 if(bl)
9 {
10 printf("uninitial ");
11 bl = false;
12 }
13 else{
14 printf("before:%d ",nvar[0]);
15 }
16 nvar[0] = 10*i;
17 //nvar[i] = 10*i;
18 printf("after:%d\n",nvar[0]);
19 }
20 }
实际上,我主要是想看看几个问题:
1、循环体每次循环是否都会为整形数组分配内存
2、每次进入循环时,nvar[0]的值是不是都保留了上一个循环时的值
发现:
1、由于编译器的优化,不会每次循环都会为数组分配内存,而是进入for循环之前一次分配好。
2、每次进入循环时,nvar[0]的值虽然保留了上一个循环的值,但是打印这个值是会报错的,编译器提示变量undefined,因为实际上这个地址对应的变量是一个新的变量,上一个循环产生的变量已经在循环结束时析构。其实把这个数组换成一个类的对象,会更好理解,如下:
1 class B
2 {
3 private:
4 int b;
5 public:
6 B()
7 {
8 printf("construction function\n");
9 }
10 B(B &b)
11 {
12 printf("copy construction function\n");
13 }
14 ~B()
15 {
16 printf("destruction function\n");
17 }
18 };
19
20 int main()
21 {
22 B class1;
23 for(int i=0; i<3; i++)
24 {
25 B bclass1;
26 for (i=0; i<3; i++)
27 {
28 B bclass2 = bclass1;
29 }
30 }
结果:

在回到上面的那个问题,当我把循环体中数据的复制方式换成nvar[i] = 10*i时,就不会产生运行错误。这是为什么?难道这样赋值时,上一次循环的临时对象不会析构吗?
我的回答:
临时变量每次循环回收是肯定回收的。我研究了一下,你这个错误是运行时错误,就是未初始化时出现的,属于编译器问题。vc上如果打开了RTC运行时检查就会报这个错,但是gcc上就不会。这个错,我看了一下汇编代码,还是跟编译器有关。编译器在开始的时候给为赋值的数组打了一个标志位。
1 mov BYTE PTR $T3913[ebp], 0
而循环进行第二次的时候,
1 cmp BYTE PTR $T3913[ebp], 0 2 jne SHORT $LN8@main 3 push OFFSET $LN9@main 4 call __RTC_UninitUse 5 add esp, 4
比较发现该数组依然没有初始化,就到了__RTC_UninitUse这个函数报了个错。诡异的是
当
nvar[i] = 10*i;
时编译器没有打初始化的标志位。所以也不会报错。原因可能跟编译器有关。
结论是,不要把未定义的行为交给编译器去处理。
自己印证了下, linux:
class BASE {
public:
int a;
~BASE() {
printf("~BASE\n");
}
};
int main() {
for (int i = 0; i <= 2; i++) {
BASE b[2];
printf("%d\n", b[1].a);
b[1].a = 9999;
printf("%p\n", b);
}
}输出:
0
0x7fffd1896420
~BASE
~BASE
9999
0x7fffd1896420
~BASE
~BASE
9999
0x7fffd1896420
~BASE
~BASE
对象必然会被解析,这是规范规定的,但是确实空间是被重复利用的.
本文通过实验探讨了循环体内局部变量的内存分配机制,包括每次循环是否重新分配内存及变量值是否保留等问题,并分析了编译器优化对局部变量的影响。

783

被折叠的 条评论
为什么被折叠?



