首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 其他教程 > 其他相关 >

静态储存区和堆栈解析

2013-03-28 
静态存储区和堆栈解析以下均来自网络高手所列举出来的问题,绝非自己写作。只是自己也有不太深入了解的地方。

静态存储区和堆栈解析

以下均来自网络高手所列举出来的问题,绝非自己写作。只是自己也有不太深入了解的地方。作为笔录,以待深入研究学习!

*******************************************************************************************************************************************************

下面的例子说明指针和数组的区别

      4、堆区(heap)堆是应用程序在运行的时候请求操作系统分配给自己内存,由于从操作系统管理的内存分配,所以在分配和销毁时都要占用时间,因此用堆的效率非常低.但是堆的优点在于,编译器不必知道要从堆里分配多少存储空间,也不必知道存储的数据要在堆里停留多长的时间,因此,用堆保存数据时会得到更大的灵活性。事实上,面向对象的多态性,堆内存分配是必不可少的,因为多态变量所需的存储空间只有在运行时创建了对象之后才能确定.在C++中,要求创建一个对象时,只需用new命令编制相关的代码即可。执行这些代码时,会在堆里自动进行数据的保存.当然,为达到这种灵活性,必然会付出一定的代价:在堆里分配存储空间时会花掉更长的时间!这也正是导致效率低的原因。 什么是常见的堆性能问题?以下是使用堆时会遇到的最常见问题: 1.分配操作造成的速度减慢。光分配就耗费很长时间。最可能导致运行速度减慢原因是空闲列表没有块,所以运行时分配程序代码会耗费周期寻找较大的空闲块,或从后端分配程序分配新块。2.释放操作造成的速度减慢。释放操作耗费较多周期,主要是启用了收集操作。收集期间,每个释放操作“查找”它的相邻块,取出它们并构造成较大块,然后再把此较大块插入空闲列表。在查找期间,内存可能会随机碰到,从而导致高速缓存不能命中,性能降低。3.堆竞争造成的速度减慢。当两个或多个线程同时访问数据,而且一个线程继续进行之前必须等待另一个线程完成时就发生竞争。竞争总是导致麻烦;这也是目前多处理器系统遇到的最大问题4.堆破坏造成的速度减慢。造成堆破坏的原因是应用程序对堆块的不正确使用。通常情形包括释放已释放的堆块或使用已释放的堆块,以及块的越界重写等明显问题。5.频繁的分配和重分配造成的速度减慢。这是使用脚本语言时非常普遍的现象。如字符串被反复分配,随重分配增长和释放。不要这样做,如果可能,尽量分配大字符串和使用缓冲区。另一种方法就是尽量少用连接操作 

    5、程序代码区

    —存放函数体的二进制代码。 

    #include<stdio.h>

    int g1=0,g2=0, g3=0;

    int main()

    {

    static int s1=0,s2=0, s3=0;

    int v1=0,v2=0, v3=0;

    //打印出各个变量的内存地址

    printf("0x%08x\n",&v1);//打印各本地变量的内存地址

    printf("0x%08x\n",&v2);

    printf("0x%08x\n\n",&v3);

    printf("0x%08x\n",&g1);//打印各全局变量的内存地址

    printf("0x%08x\n",&g2);

    printf("0x%08x\n\n",&g3);

    printf("0x%08x\n",&s1);//打印各静态变量的内存地址

    printf("0x%08x\n",&s2);

    printf("0x%08x\n\n",&s3);

    return0;

    }

     静态储存区和堆栈解析 输出的结果就是变量的内存地址。其中v1,v2,v3是本地变量,g1,g2,g3是全局变量,s1,s2,s3是静态变量。你可以看到这些变量在内存是连续分布的,但是本地变量和全局变量分配的内存地址差了十万八千里,而全局变量和静态变量分配的内存是连续的。这是因为本地变量和全局/静态变量是分配在不同类型的内存区域中的结果。全局变量和静态变量分配在静态数据区,本地变量分配在动态数据区,即堆栈中。程序通过堆栈的基地址和偏移量来访问本地变量。  例子程序  int a = 0;//全局初始化区  char *p1;//全局未初始化区  main()     {       int b;//栈       chars[] = "abc";//栈       char*p1,*p2;//栈       char*p3 = "123456";   //123456\0在常量区,p3在栈上。    staticint c =0;//全局(静态)初始化区    p1 =(char *)malloc(10);      p2 =(char *)malloc(20);    strcpy(p1,"123456");    }     分配得来得10和20字节的区域就在堆区。     strcpy(p1,"123456"); 123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。  静态存储区和堆栈我们通过代码段来看看对这样的三部分内存需要怎样的操作和不同,以及应该注意怎样的地方。 

    #include<stdio.h>

    char *get_str(){  char*str="abcd";   //"abcd"在文字常量区  return str;                //返回指向文字常量区的指针}void main(){  char *p=get_str();  printf(p);}//执行正确,指向的是静态存储区常量 char *p = “HelloWorld1”;char a[] = “HelloWorld2”;p[2] =‘A’;a[2] =‘A’;char*p1 = “HelloWorld1;”静态储存区和堆栈解析 这个程序是有错误的,错误发生在p[2] =‘A’这行代码处,为什么呢,是变量p和变量数组a都存在于栈区的(任何临时变量都是处于栈区的,包括在main()函数中定义的变量)。但是,数据“HelloWorld1”和数据“HelloWorld2”是存储于不同的区域的。  因为数据“HelloWorld2”存在于数组中,所以,此数据存储于栈区,对它修改是没有任何问题的。因为指针变量p仅仅能够存储某个存储空间的地址,数据“HelloWorld1”为字符串常量,所以存储在静态存储区。虽然通过p[2]可以访问到静态存储区中的第三个数据单元,即字符‘l’所在的存储的单元。但是因为数据“HelloWorld1”字符串常量,不可以改变,所以在程序运行时,会报告内存错误。并且,如果此时对p和p1输出的时候会发现p和p1里面保存的地址是完全相同的。换句话说,在数据区只保留一份相同的数据。  char *str=NULL; //str是在栈区中   str=(char *)malloc(MAX*sizeof(char));  //str申请的空间在堆中  strcpy(str,"Welcome toC++ world!");  //将静态存储区的字符串复制给str,不是指针指向  str[2] = 'a';//这样就可以修改str的内容了,此时                   //静态存储区的字符串已复制到堆中  printf("%s\n",str); 1.申请方式stack:由系统自动分配。 例如,声明在函数中一个局部变量 int b;系统自动在栈中为b开辟空间   heap: 需要程序员自己申请,并指明大小,在c中malloc函数如p1 =(char *)malloc(10);在C++中用new运算符如p2 =new char[20];//(char *)malloc(10);但是注意p1、p2本身是在栈中的。 2.申请后系统的响应栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。  堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。 3.请大小的限制栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。   堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。 4.申请效率的比较栈由系统自动分配,速度较快。但程序员是无法控制的。   堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.  另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈,而是直接在进程的地址空间中保留一块内存,虽然用起来最不方便。但是速度快,也最灵活 5.堆和栈中的存储内容栈:在函数调用时,第一个进栈的是主函数中函数调用后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。  当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。   堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。 void func(int param1, int param2,int param3){ int var1=param1; int var2=param2; int var3=param3; //打印出各个变量的内存地址printf("0x%08x\n",&param1); printf("0x%08x\n",&param2); printf("0x%08x\n\n",&param3); printf("0x%08x\n",&var1); printf("0x%08x\n",&var2); printf("0x%08x\n\n",&var3); } void main() { func(1,2,3); } 静态储存区和堆栈解析   6、存取效率的比较chars1[] = "aaaaaaaaaaaaaaa"; char *s2 = "bbbbbbbbbbbbbbbbb";aaaaaaaaaaa是在运行时刻赋值的;而bbbbbbbbbbb是在编译时就确定的;但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。比如:存取效率的比较对应的汇编代码   10: a= c[1]; 004010678A 4D F1 movcl,byteptr[ebp-0Fh]0040106A88 4D FC mov byteptr[ebp-4],cl11: a= p[1]; 0040106D8B 55 EC movedx,dwordptr[ebp-14h]004010708A 42 01 moval,byteptr[edx+1]0040107388 45 FC mov byteptr[ebp-4],al第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到edx中,在根据edx读取字符,显然慢了。总之,对于堆区、栈区和静态存储区它们之间最大的不同在于,栈的生命周期很短暂。但是堆区和静态存储区的生命周期相当于与程序的生命同时存在(如果您不在程序运行中间将堆内存delete的话),我们将这种变量或数据成为全局变量或数据。但是,对于堆区的内存空间使用更加灵活,因为它允许你在不需要它的时候,随时将它释放掉,而静态存储区将一直存在于程序的整个生命周期中。           

     

     

热点排行