数据在内存中如何存储?静态存储区 栈区 堆区 小端和大端存储方式

一、静态存储区、栈区和堆区

        一般来说,编译器将内存分为三部分:静态存储区域、栈、堆。静态存储区主要保存 全局变量和静态变量,栈存储调用函数相关的变量、地址等,堆存储动态生成的变量,在c中是指由malloc,free运算产生释放的存储空间,在c++中 就是指new和delete运算符作用的存储区域。

1、  静态存储分配

  1) 指在编译时给数据对象分配固定的存储位置,运行时始终不变。即一旦存储空间的某个位置分配给了某个数据名,则在目标程序的整个运行过程中,此位置(地址)就属于该数据名。
  2) 由静态存储分配产生的数据区称为静态数据区。
  3) 静态存储分配适用于不允许递归过程或递归调用,不允许可变体积的数据结构的语言。
  4) 静态存储分配的特点:简单、易于实现。
  5) 用来存储初始化的全局变量初始化的静态变量
2、  动态存储分配

  1) 指在运行阶段动态地为源程序中的数据对象分配存储位置。
  2) 实行动态存储分配的语言具有的特点:
允许递归过程
允许可变数据结构(可变数组或记录等)
允许用户自由申请和释放空间
 3) 这种程序在编译时无法确定运行时所需数据空间的大小,需待程序运行时动态确定
 4) 有两种动态存储分配方式:栈式(stack)、堆式(heap)。

3、  栈式动态存储分配

 1) 在数据空间中开辟一个栈区,每当调用一个过程时,它所需要的数据空间就分配在栈顶,每当过程工作结束时就释放这部分空间。空间的使用符合先借后还的原则。

 2) 特点:先借后还,管理简单,空间使用效率高
 3) 用来存储函数的参数静态局部变量


4、  堆式动态存储分配

1) 在数据空间中开辟一片连续的存储区(通常叫做堆),每当需要时就从这片空间借用一块,不用时再退还。借用与归还未必服从“先借后还”的原则。
2) 堆式动态存储分配适合于用户可以自由申请和归还数据空间的语言,如C++。
3) 特点:适用范围广,容易出现碎片。如何充分利用空间是个难题。

4) malloc,free运算产生释放的存储空间,以及c++中new和delete运算符产生释放的存储空间都是堆空间,未初始化的全局变量和静态变量也存储在该区

常见疑惑:

常量字符串很少需要修改,存在静态存储区会提高程序的效率

const char str1[] = "abc";  //str1为数组变量,有自己的内存空间。

const char *str2 = "abc";  //str2为指针,指向静态存储区的"abc"字符串常量。

注意上述语句的区别,具体范例如下:

范例一:

#include <stdio.h> 
int A() 

        int test=10; 
        return test; 

int main() 

        int a=A(); 
        printf("%d/n",a); 
        return 0; 
}  
       上面的代码能编译通过  我想问 在A() 函数中的 test 变量的生存期不是只在A()函数体内吗?  怎么还能成功返回呢 ?下面的这段代码为何就不行呢 两个程序中的变量生存期有什么区别啊? 
#include <stdio.h> 
char* A() 

        char p[]="hello world"; 
        return p; 

int main() 

        char *str=NULL; 
        str=A(); 
        printf("%s",str); 
}

答案是:

关键在int a=A(); 实际是a=test;之后test挂掉了,但是a还存在. 

而str=A(); 就是str=p. 之后p跟p指向的堆区都挂了,可是str依然指向p那片挂掉的堆区. 

范例二:

C/C++ code

   
   
char * FuncC() { char* a="hello word" ; return a; } char * FuncB() { char a[]="hello word" ; return a; }
char *FuncD()
{  
static char a[]="hello word";  
return a;
} int _tmain(int argc, _TCHAR* argv[]) { char *b,* c, *d; c = FuncC(); b = FuncB();
d = FuncD() char a[100 ]; ::memset(a,NULL,100 ); strcpy(a,c); std::cout<<"A="<<a <<" B="<<b<<" C ="<<c <<"D="<<d<< endl; getchar(); return 0 ; }

输出结果: 

   
   
A=hello word B=p_/*(操作未知)*/ C=hello word D=hello word;

为什么FuncB失败。FuncC和FuncD就成功呢? 

解释:

       FuncC中,char* a="hello word"; a并没有分配空间,"hello word"串放在静态区,放在栈中的只是指针a(4个字节的指针),函数返回后,销毁的也只是a,静态区的串是不会销毁的 。调用c = FuncC()后,虽然a被销毁了,但c接收了返回值,依然指向静态区的串"hello word" 
       
FuncB的做法相当于在函数的栈内申请了一个数据区存放a[]="hello word"; 存在一个数据拷贝的操作:将静态数据区内的数据拷贝到栈数据区内,调用b = FuncB()后,b指向堆栈中的数据,而不是静态区的数据;当函数退出后,栈就销毁了,那b指向的数据就是未知的了。


二、小端和大端存储方式

1.什么是大端和小端

 1) Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端,反序排序。。
 2) Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端,依次排序。

2.数字0x12 34 56 78在内存中的表示形式

 以下实例都是在Intel x86 CPU上的Windows为讲解平台(小端存储,假设内存地址从0x4000开始)。

内存地址 小端模式存放内容  
0x4000 0x78  
0x4001 0x56  
0x4002 0x34  
0x4003 0x12  

3.字符串在内存中的表示形式

测试程序1:测试字符串在内存中的存储位置。
  1.  
  2.         char *ptr1, *ptr2;
  3.         ptr1 = "language"; ptr2 = "programe";

  4.         int k;
  5.         for (= 0; k < 18; k++){
  6.                 printf("0x%d:", ptr1 + k);
  7.                 printf("%c\n", *(ptr1 + k));
  8.         }
  9.         printf("\n");

输出结果为:

  1. [root@deron Mar9]# ./yabakui 
  2. 0x8048584:l
  3. 0x8048585:a
  4. 0x8048586:n
  5. 0x8048587:g
  6. 0x8048588:u
  7. 0x8048589:a
  8. 0x804858a:g
  9. 0x804858b:e
  10. 0x804858c:
  11. 0x804858d:p
  12. 0x804858e:r
  13. 0x804858f:o
  14. 0x8048590:g
  15. 0x8048591:r
  16. 0x8048592:a
  17. 0x8048593:m
  18. 0x8048594:e
  19. 0x8048595:

结论:相邻字符串按照地址上升存储,(即先申请先分配)且字符串字符按照地址上升来存放

如图:(下图跟内存一样,按照低地址在下来看)

4.字符串在内存中的表示形式

测试程序2:测试字符数组在内存中的存储位置

  1.   
  2.         char a[] = "language", b[] = "programe"; // a[]= "12345678"存储方式也是数组元素按照地址地址上升存储
  3.         char *ptr1, *ptr2;
  4.         ptr1 = a;
  5.         ptr2 = b;
  6.         int k;
  7.         for (= 0; k < 18; k++){
  8.                 printf("0x%x:", ptr2 + k);
  9.                 printf("%c\n", *(ptr2 + k));
  10.         }
  11.         printf("\n");

输出结果:

  1. [root@deron Mar9]# ./yabakui 
  2. 0xbf972d06:p
  3. 0xbf972d07:r
  4. 0xbf972d08:o
  5. 0xbf972d09:g
  6. 0xbf972d0a:r
  7. 0xbf972d0b:a
  8. 0xbf972d0c:m
  9. 0xbf972d0d:e
  10. 0xbf972d0e:
  11. 0xbf972d0f:l
  12. 0xbf972d10:a
  13. 0xbf972d11:n
  14. 0xbf972d12:g
  15. 0xbf972d13:u
  16. 0xbf972d14:a
  17. 0xbf972d15:g
  18. 0xbf972d16:e
  19. 0xbf972d17:

结论:相邻两个字符数组按照地址下降存储(即后申明先分配),数组元素按照地址地址上升存储

如图:

通过比较字符串和字符数组在内存地址,可以知道,它们存放在内存的不同区域。







  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值