今天来进一步了解一下C/C++的存储相关的东西。
首先,编程有哪几种数据区域?
其次,C/C++怎样来定义不同区域的数据?
再次,这几种区域的数据的生存周期是怎样的?
最后,不同区域的数据的访问规则是什么?
首先,相信大家都应该知道,静态区域、堆栈区域和堆区域数据,我个人的理解都是物理内存地址,适当的做了区域划分,而这三类地址为编程常用内存存储,而另外还有一类特殊的高效存储应该就是寄存器。
其次,我们了解下这三种存储的定义方式
1、静态区域数据,通过static或者extern(默认不带,或者带)定义的内容,例如:
// static 定义的静态区数据测试
static int index = 1000 ;
static int iniIndex = 2000 ;
void getYear(void)
{
static int year = 2012;
printf( "STATIC MEM:year@0x%p \t\t= %d\r\n",&year,year );
}
int main(int argc, char** argv)
{
static int age = 27 ;
int day = 21;
int month = 8;
printf( "STACK MEM:day@0x%p \t\t= %d\r\n",&day,day );
printf( "STACK MEM:month@0x%p \t\t= %d\r\n",&month,month );
printf( "STATIC MEM:index@0x%p \t\t= %d\r\n",&index,index );
printf( "STATIC MEM:iniIndex@0x%p \t\t= %d\r\n",&iniIndex,iniIndex );
printf( "STATIC MEM:age@0x%p \t\t= %d\r\n",&age,age );
getYear();
return 0;
}
// 输出结果
STACK MEM:day@0x0012FF60 = 21 // 堆栈数据信息,和static定义的区域内存地址不一样
STACK MEM:month@0x0012FF54 = 8 // 堆栈数据信息,和static定义的区域内存地址不一样
STATIC MEM:index@0x00419034 = 1000 // 虽然在不同代码段中用static定义,但是内存是连续的
STATIC MEM:iniIndex@0x00419038 = 2000 // 虽然在不同代码段中用static定义,但是内存是连续的
STATIC MEM:age@0x00419040 = 27 // 虽然在不同代码段中用static定义,但是内存是连续的
STATIC MEM:year@0x0041903C = 2012 // 虽然在不同代码段中用static定义,但是内存是连续的
请按任意键继续. . .
// extern 定义的静态区域数据测试
extern int exINT = 10000; // 在 static int iniIndex = 2000 ; 添加
int defaultExInt = 100000;
printf( "STATIC MEM:iniIndex@0x%p \t\t= %d\r\n",&exINT,exINT ); // 在 printf( "STATIC MEM:iniIndex@0x%p \t\t= %d\r\n",&iniIndex,iniIndex );后添加。
printf( "STATIC MEM:iniIndex@0x%p \t\t= %d\r\n",&defaultExInt,defaultExInt );
//输出结果
STACK MEM:day@0x0012FF60 = 21
STACK MEM:month@0x0012FF54 = 8
STATIC MEM:index@0x00419034 = 1000
STATIC MEM:iniIndex@0x00419038 = 2000
STATIC MEM:exINT@0x0041903C = 10000
STATIC MEM:defaultExInt@0x00419040 = 100000
STATIC MEM:age@0x00419048 = 27
STATIC MEM:year@0x00419044 = 2012
请按任意键继续. . .
//C/C++中,默认全局的定义默认类型是extern,所以我们会得出 exINT和defaultExInt都会在静态区域中。
// 总结,凡通过static或者extern(包括不加extern的)都会在静态区域中定义数据。
2、堆栈数据,当然就是我们定义的程序块中的临时数据,作用域最小。
3、堆上数据,这部分是由我们自动申请、释放的内存。例如:
// heap memory ;在上面程序段添加
int *mallocData = new int[2];
memset( mallocData,0,2*sizeof(int) );
printf( "HEAP MEM:mallocData1@0x%p \t\t= %d\r\n",&mallocData[0],mallocData[0] );
printf( "HEAP MEM:mallocData2@0x%p \t\t= %d\r\n",&mallocData[1],mallocData[1] );
delete[] mallocData;
mallocData = NULL;
// 输出结果
// 堆栈内存分布
STACK MEM:day@0x0012FF60 = 21
STACK MEM:month@0x0012FF54 = 8
// 静态区域内存分布
STATIC MEM:index@0x00419034 = 1000
STATIC MEM:iniIndex@0x00419038 = 2000
STATIC MEM:exINT@0x0041903C = 10000
STATIC MEM:defaultExInt@0x00419040 = 100000
STATIC MEM:age@0x00419048 = 27
STATIC MEM:year@0x00419044 = 2012
// 自定义内存分布
HEAP MEM:mallocData1@0x003956F8 = 0
HEAP MEM:mallocData2@0x003956FC = 0
请按任意键继续. . .
总结,物理内存是单一的,对程序而言却可以定义不同区域的内存。
另外,还有一类特殊存储,寄存器存储。register定义的变量,该变量会被在CPU寄存器容量允许的情况下直接被加载。CPU访问内存单元时,先见内存单元的地址加载到地址总线(AB)上,同时将内存电路的“读写控制”设置为有效,然后内存单元中的数据就通过数据总线(DB)“流”向了接受的寄存器中,或方向流向内存单元中。这样理解就可以知道register的好处。但CPU寄存器地址毕竟是有限的,所以在高性能优化时可以适量采用。
再次,我们在来了解这三种区域的生存周期和作用域。
对于生命周期的理解,分为永久性、临时性和自由性。对于我们静态区域的数据,我们一旦定义了就永久的存在静态内存区域中;而堆栈区域的数据,具有临时性,在程序块的作用范围内,然而对于自由区域数据就完全靠我们自己来申请和释放,如果不用又不释放那就产生内存垃圾,如果这段程序段又在整个生命期不断被调用,那么内存就不断被消耗,就是所谓的内存泄漏。
对于数据区的周期有所了解后,其实对作用域也显而易见,整个生命期都存在的内存肯定可以被当前单一编译单元操作,临时性的内存不具备可控性,只能在其生命期内可以操控,所以就看你怎么确定他的生命期,只要在生命期内都可以操控。
最后,在分享一下在不同编译单元中的作用域问题。
这里需要提到 extern关键字,当一个变量的声明中指定了extern时候,这个变量就可以被被引用的编译单元访问。例如:
a.h
extern int g_NUMBER;
a.cpp
int g_NUMBER = 10000;
m.cpp
#include "a.h"
int main( int agrc,char** argv )
{
printf( "g_NUMBER@0x%p \t= %d\r\n",&g_NUMBER,g_NUMBER );
return 0;
}
谢谢。