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
对象必然会被解析,这是规范规定的,但是确实空间是被重复利用的.