一个程序启动,操作系统为程序提供4GB的虚拟内存,用一个32位2进制数来表示这片内存。(并不是4GB都供用户访问,后2GB及前2GB的部分不能访问,0x00000000不能访问)
2^32=4 294 967 296=4*1024(G)*1024(M)*1024(K)
0x00000000(低地址)–0xFFFFFFFF(高地址),共2^32个地址,地址加1,对应下一个字节。每个地址的容量是1Byte,所以32位二进制数可表示4GB内存。
1、栈区(stack)
编译器自动分配释放内存,存放程序临时创建的局部变量,函数的参数、函数返回值等。分配内存时分配一块连续的内存区域,如果栈内空间小于所申请的空间大小,那么这时系统将揭示栈溢出,并给出相应的异常信息。栈由高位地址往低位地址扩展。
int main()
{
int array[3] = { 1,2,3 };
cout << &array[0] << " " << &array[1] << " " << &array[2] << endl;
int a1 = 0, a2 = 0, a3 = 0;
cout << "a1:" << &a1 << " a2: " << &a2 << " a3: " << &a3 << endl;
system("pause");
return 0;
}
系统分配一块连续的内存存储局部变量的值,由高位地址向低位地址扩展。数组先存array[2]最后存array[0],大端模式。数组元素地址相差刚好4个字节;
连续定义的int 变量之间地址相差值大于4个字节(6byte),可能是编译器预留一些地址作他用。
2、堆区(heap)
用于存放进程运行中动态分配的内存段,它的大小并不固定,可动态扩张或缩减。由程序员手动申请内存(new)、释放内存(delete), 若程序员不释放,程序结束时可能由操作系统回收。类似于链表,不同区域的内存块通过指针链接起来的,在内存中不是一块连续的内存。一个节点从链中断开,就需要把断开的节点从内存中释放。由低位地址向高位地址扩展。
int main()
{
int a1 = 0, a2 = 0, a3 = 0;
cout << "&a1:" << &a1 << " &a2 " << &a2 << " &a3 " << &a3 << endl;
int *p1 = new int(a1);//堆区
int *p2 = new int(a2);
int *p3 = new int(a3);
cout << "&a1:" << p1 << " &a2 " << p2 << " &a3 " << p3 << endl;
cout << "&p1:" << &p1 << " &p2:" << &p2 << " &p3:" << &p3 << endl;
system("pause");
return 0;
}
第二行a1,a2,a3的地址不连续,因为a1,a2,a3存放在堆中。
////// 没有按照从低地址向高地址扩展,还没找到原因。
第三行结果地址连续,int *p指针p本身是在栈中。
3、全局区/静态区(static)
程序编译阶段已经分配好内存空间并初始化,在程序整个运行期都存在。存放全局变量和静态变量。初始化的全局变量和静态变量在一块区域, 静态存储区内的变量若不显示初始化,则编译器会自动以默认的方式进行初始化,即静态存储区内不存在未初始化的变量。变量一经初始化不可修改。
static int s1=0, s2=0, s3=0;// 静态全局变量
int g1=0, g2=0, g3=0;//全局变量
int main()
{
int a1 = 0, a2 = 0, a3 = 0;
cout << "a1:" << &a1 << " a2: " << &a2 << " a3: " << &a3 << endl;
int *p1 = new int(a1);
cout << "a1:" << p1 << endl;
cout << "s1:" << &s1 <<" s2:"<< &s2 <<" s3"<< &s3 << endl;
cout << "g1:" << &g1 << " g2:" << &g2 << " g3:" << &g3 << endl;
system("pause");
return 0;
}
局部变量,从堆中动态分配内存的变量,静态常量(和全局变量)分别处于三个不同的内存段。
static int s1=0, s2=0, s3=0;// 静态全局变量
int g1=0, g2=0, g3=0;//全局变量
const int cc = 0; //const全局变量
int main()
{
const int ccc = 0; //const 局部变量
int a1 = 0; //局部变量
static int s = 0; //静态局部变量
cout << "a1:" << &a1 << endl;
int *p1 = new int(a1);
cout << "a1:" << p1 <<endl;
cout << "g1:" << &g1 << " g2:" << &g2 << " g3:" << &g3 << endl;
cout <<"s:"<< &s<< endl;
cout << "const:" << &cc << endl;
cout << "const1:" << &ccc << endl;
system("pause");
return 0;
}
全局const变量存放在静态区,局部const变量存放在栈区
总之,静态变量与全局变量存储在静态/全局区。
4、程序代码区
又称只读区。存放程序编译后的二进制码,这部分区域的大小在程序运行前就已经确定,通常属于只读。
其他问题:
动态区:函数调用时申请空间,函数调用结束后释放空间。
静态区:程序被加载时,系统分配空间,函数调用结束后分配的空间仍然存在。
静态存储区的变量具备持久性和默认值0。
static的作用:
(百度百科很全了)
注意:全局变量和全局静态变量的区别**
1)全局变量是不显式用static修饰的全局变量,全局变量默认是有外部链接性的,作用域是整个工程,在一个文件内定义的全局变量,在另一个文件中,通过extern全局变量名的声明,就可以使用全局变量。
2)全局静态变量是显式用static修饰的全局变量,作用域是声明此变量所在的文件,其他的文件即使用extern声明也不能使用。
静态局部变量有以下特点:
该变量在全局数据区分配内存;
静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化;
静态局部变量一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为0;
它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束;
静态函数
在函数的返回类型前加上static关键字,函数即被定义为静态函数。静态函数与普通函数不同,它只能在声明它的文件当中可见,不能被其它文件使用。
静态数据成员
因为静态数据成员在全局数据区分配内存,属于本类的所有对象共享,所以,它不属于特定的类对象,在没有产生类对象时其作用域就可见,即在没有产生类的实例时,我们就可以操作它;
静态数据成员主要用在各个对象都有相同的某项属性的时候。比如对于一个存款类,每个实例的利息都是相同的。所以,应该把利息设为存款类的静态数据成员。这 有两个好处,第一,不管定义多少个存款类对象,利息数据成员都共享分配在全局数据区的内存,所以节省存储空间。第二,一旦利息需要改变时,只要改变一次, 则所有存款类对象的利息全改变过来了
静态成员函数
与静态数据成员一样,我们也可以创建一个静态成员函数,它为类的全部服务而不是为某一个类的具体对象服务。
静态成员函数由于不是与任何的对象相联系,因此它不具有this指
针。从这个意义上讲,它无法访问属于类对象的非静态数据成员,也无法访问非静态成员函数,它只能调用其余的静态成员函数。