本来以为不会遇到这个蠢问题:一个局部变量可以在其作用域之外访问么?但是在仿写别人的stl的时候,发现他有这个问题,而这个问题又引发了另一个问题:为啥指针访问临时变量地址总不变?
啊,对了,这是上面的问题的答案:
局部变量确实是不可以被外部使用的。
这种情况只是恰巧那块内存区是存在的,但它并不属于 foo 函数里的 a 所有,
在实际编程中一定不要这么用,很大概率程序会崩溃。
下面就是我用来探究这个问题的小程序:
#include <iostream>
class node{
public:
node(int a):i(a){}
int i;
};
class node_iterator{
public:
node_iterator(node* a):i(a){}
node* i;
};
node_iterator test(node i){
node a(i);
return node_iterator(&a);
}
void stack_occupy(){
int i = 1;
//int j[120];
std::cout << &i << std::endl;
std::cout << "5: " << test(5).i << std::endl;
std::cout << "6: " << test(6).i << std::endl;
}
int main(){
node *tem1, *tem2;
std::cout << "1: " << test(1).i << std::endl;
if((tem1=test(2).i)==(tem2=test(3).i)) std::cout << "equal " << "2: " << tem1 << " 3: " << tem2 << std::endl;
std::cout << "4: " << test(4).i << std::endl;
stack_occupy();
std::cout << "7: " << test(7).i << std::endl;
return 0;
}
注释掉int j[120];
:
1: 0x61fec8
equal 2: 0x61fec8 3: 0x61fec8
4: 0x61fec8
0x61fec4
5: 0x61fe98
6: 0x61fe98
7: 0x61fec8
做实验时,我又发现了新的问题:变量在栈内存中的排布其实并不是完全连续的,似乎和命名空间、作用域之类的有关,比如函数stack_occupy()
里的int i
在栈内存中就是紧挨着之前的临时变量反复使用的空间0x61fec8
的。但是在该函数里又产生了一层作用域(也就是在stack_occupy里再调用test)时,会空出一大段空间。这种分层分块的做法让我想到了STL中Alloc使用的内存池技术,大概这种分配内存的方式编译效率更高吧,不用多一个变量申请一次。我算了一下,间隔为0x30,换算成十进制,也就是480byte。对于我的g++版本来说,一个int是4byte,所以是120个int。我就想看看这一个作用域要是被填满了,下一个作用域的内存起点是多少,就把int j[120];
的注释删掉了:
1: 0x61fec8
equal 2: 0x61fec8 3: 0x61fec8
4: 0x61fec8
0x61fec4
5: 0x61fcb8
6: 0x61fcb8
7: 0x61fec8
始终要注意:除了动态申请的内存(在堆内存里),在其他区域的内存(不止栈内存,还有静态区之类的)在程序运行前,都被编译器分配好了。爱怎么分配是编译器决定的,我们现在这个例子把几次局部变量都分配在同一个起点,不一定每个编译器次次都这样,怎么做完全取决于编译器是怎么设计的。我举个例子:编译器可能认为(因为是我猜的)需要反复申请 node
里int
大小的内存(就你1234567的操作),所以正常程序运行时,本来int i
也可以放在0x61fec8这个位置的(因为之前的临时变量已经销毁了),然后7号操作申请的临时变量排在int i
后面,但编译器并没有这么做,因为编译器认为把这个位置空出来留给后面的可能效率更高。这完全取决于编译器怎么设计。