在冯诺依曼的体系结构中,一个进程必须有:代码段,堆栈段,数据段。

进程的虚拟地址空间图示如下:

堆栈段:

  1. 为函数内部的局部变量提供存储空间。

  2. 进行函数调用时,存储“过程活动记录”。

  3. 用作暂时存储区。如计算一个很长的算术表达式时,可以将部分计算结果压入堆栈。

数据段(静态存储区):

  包括BSS段(Block Started by Symbol)的数据段。BSS段存储未初始化或初始化为0的全局变量、静态变量,具体体现为一个占位符,并不给该段的数据分配空间,只是记录数据所需空间的大小。数据段存储经过初始化的全局和静态变量。

  1. #define DEBUG "debug"
  2. int space[1024][1024];
  3. int data = 1;
  4. int no_data = 0;
  5. int main()
  6. {
  7. char *a = DEBUG;
  8. return 1;
  9. }

使用nm查看后

  1. 0000000000600660 d _DYNAMIC
  2. 00000000006007f8 d _GLOBAL_OFFSET_TABLE_
  3. 0000000000400578 R _IO_stdin_used
  4. w _Jv_RegisterClasses
  5. 0000000000600640 d __CTOR_END__
  6. 0000000000600638 d __CTOR_LIST__
  7. 0000000000600650 D __DTOR_END__
  8. 0000000000600648 d __DTOR_LIST__
  9. 0000000000400630 r __FRAME_END__
  10. 0000000000600658 d __JCR_END__
  11. 0000000000600658 d __JCR_LIST__
  12. 0000000000600820 A __bss_start
  13. 0000000000600818 D __data_start
  14. 0000000000400530 t __do_global_ctors_aux
  15. 00000000004003e0 t __do_global_dtors_aux
  16. 0000000000400580 R __dso_handle
  17. w __gmon_start__
  18. 0000000000600634 d __init_array_end
  19. 0000000000600634 d __init_array_start
  20. 0000000000400490 T __libc_csu_fini
  21. 00000000004004a0 T __libc_csu_init
  22. U __libc_start_main@@GLIBC_2.2.5
  23. 0000000000600820 A _edata
  24. 0000000000a00840 A _end
  25. 0000000000400568 T _fini
  26. 0000000000400358 T _init
  27. 0000000000400390 T _start
  28. 00000000004003bc t call_gmon_start
  29. 0000000000600820 b completed.6347
  30. 000000000060081c D data
  31. 0000000000600818 W data_start
  32. 0000000000600828 b dtor_idx.6349
  33. 0000000000400450 t frame_dummy
  34. 0000000000400474 T main
  35. 0000000000600830 B no_data
  36. 0000000000600840 B space

可以看到变量data被分配在data段,而被初始化为0的no_data被分配在了BSS段。

  .bss是不占用.exe文件空间的,其内容由操作系统初始化(清零);而.data却需要占用,其内容由程序初始化。

  注意:.data和.bss在加载时合并到一个Segment(Data Segment)中,这个Segment是可读可写的。

代码段:

  又称为文本段。存储可执行文件的指令;也有可能包含一些只读的常数变量,例如字符串常量等。

  .rodata段:存放只读数据,比如printf语句中的格式字符串和开关语句的跳转表。也就是你所说的常量区。例如,全局作用域中的 const int ival = 10,ival存放在.rodata段;再如,函数局部作用域中的printf("Hello world %d\n", c);语句中的格式字符串"Hello world %d\n",也存放在.rodata段。

但是注意并不是所有的常量都是放在常量数据段的,其特殊情况如下:

  1)有些立即数与指令编译在一起直接放在代码段。

  1. int main()
  2. {
  3. int a = 10;
  4. return 1;
  5. }


a是常量,但是它没有被放入常量区,而是在指令中直接通过立即数赋值

  2)对于字符串常量,编译器会去掉重复的常量,让程序的每个字符串常量只有一份。

  1. char *str = "123456789";
  2. char *str1 = "helloworld";
  3. int main()
  4. {
  5. char* a = "helloworld";
  6. char b[10] = "helloworld";
  7. return 1;
  8. }

  汇编代码如下:

  1. .file "hello.c"
  2. .globl str
  3. .section .rodata
  4. .LC0:
  5. .string "123456789"
  6. .data
  7. .align 8
  8. .type str, @object
  9. .size str, 8
  10. str:
  11. .quad .LC0
  12. .globl str1
  13. .section .rodata
  14. .LC1:
  15. .string "helloworld"
  16. .data
  17. .align 8
  18. .type str1, @object
  19. .size str1, 8
  20. str1:
  21. .quad .LC1
  22. .text
  23. .globl main
  24. .type main, @function
  25. main:
  26. .LFB0:
  27. .cfi_startproc
  28. pushq %rbp
  29. .cfi_def_cfa_offset 16
  30. .cfi_offset 6, -16
  31. movq %rsp, %rbp
  32. .cfi_def_cfa_register 6
  33. movq $.LC1, -8(%rbp)
  34. movl $1819043176, -32(%rbp)
  35. movl $1919907695, -28(%rbp)
  36. movw $25708, -24(%rbp)
  37. movl $1, %eax
  38. leave
  39. .cfi_def_cfa 7, 8
  40. ret
  41. .cfi_endproc
  42. .LFE0:
  43. .size main, .-main
  44. .ident "GCC: (GNU) 4.4.6 20110731 (Red Hat 4.4.6-3)"
  45. .section .note.GNU-stack,"",@progbits

  可以看到str1和a同时指向.rodata段中同一个LC1

  3)用数组初始化的字符串常量是没有放入常量区的。

4)用const修饰的全局变量是放入常量区的,但是使用const修饰的局部变量只是设置为只读起到防止修改的效果,没有放入常量区。
5)有些系统中rodata段是多个进程共享的,目的是为了提高空间的利用率。

  注意:程序加载运行时,.rodata段和.text段通常合并到一个Segment(Text Segment)中,操作系统将这个Segment的页面只读保护起来,防止意外的改写。

堆:

  就像堆栈段能够根据需要自动增长一样,数据段也有一个对象,用于完成这项工作,这就是堆。堆区域是用来动态分配的内存空间,用 malloc 函数申请的,用free函数释放。calloc、realloc和malloc类似:前者返回指针的之前把分配好的内存内容都清空为零;后者改变一个指针所指向的内存块的大小,可以扩大和缩小,它经常把内存拷贝到别的地方然后将新地址返回。

栈、堆辨析:

1、栈区(stack):由编译器自动分配释放 ,存放函数的参数值、局部变量的值等。其操作方式类似于数据结构中的栈。 
2、堆区(heap):由程序员分配释放, 若程序员不释放,程序结束时可能由操作系统回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。

程序示例:

1、举个例子说明各种变量存放在什么区:

  1. #include <stdlib.h>
  2. int a=123; //a在全局已初始化数据区
  3. char *p1; //p1在BSS区(未初始化全局变量)
  4. int main()
  5. {
  6. int b; //b为局部变量,在栈区
  7. char s[]="abc"; //s为局部数组变量,在栈区
  8. //"abc"为字符串常量,存储在已初始化数据区
  9. char *p1,*p2; //p1,p2为局部变量,在栈区
  10. char *p3="123456"; //p3在栈区,"123456"在常量区(.rodata)
  11. static int c=456; //c为局部(静态)数据,在已初始化数据区
  12. //静态局部变量会自动初始化(因为BSS区自动用0或NULL初始化)
  13. p1=(char*)malloc(10); //分配得来的10个字节的区域在堆区
  14. p2=(char*)malloc(20); //分配得来的20个字节的区域在堆区
  15. free(p1);
  16. free(p2);
  17. p1=NULL; //显示地将p1置为NULL,避免以后错误地使用p1
  18. p2=NULL;
  19. }

2、我们再写一个程序,输出各变量的内存空间:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. extern void afunc(void);
  4. extern etext,edata,end;
  5. int bss_var;//未初始化全局变量存储在BSS段
  6. int data_var=42;//初始化全局存储在数据段
  7. #define SHW_ADR(ID,I) printf("The %s is at address: %8x\n",ID,&I);//打印地址宏
  8. int main(int argc,char *argv[])
  9. {
  10. char *p,*b,*nb;
  11. printf("etext address: %8x\tedata address: %8x\tend address: %8x\t\n",&etext,&edata,&end);
  12. SHW_ADR("main",main);//查看代码段main函数位置
  13. SHW_ADR("afunc",afunc);//查看代码段afunc函数位置
  14. printf("\nbss Locatoin:\n");
  15. SHW_ADR("bss_var",bss_var);//查看BSS段变量地址
  16. printf("\ndata Location:\n");
  17. SHW_ADR("data_var",data_var);//查看数据段变量地址
  18. printf("\nStack Loation:\n");
  19. afunc();
  20. printf("\n");
  21. p=(char*)alloca(32);//从栈中分配空间
  22. if(p!=NULL)
  23. {
  24. SHW_ADR("string p in stack start",*p);
  25. SHW_ADR("string p in stack end",*(p+32*sizeof(char)));
  26. }
  27. b=(char*)malloc(32*sizeof(char));//从堆中分配空间
  28. nb=(char*)malloc(16*sizeof(char));//从堆中分配空间
  29. printf("\nHeap Location:\n");
  30. SHW_ADR("allocated heap start",*b);//已分配的堆空间的起始地址
  31. SHW_ADR("allocated heap end",*(nb+16*sizeof(char)));//已分配的堆空间的结束地址
  32. printf("\np,b and nb in stack\n");
  33. SHW_ADR("p",p);//显示栈中数据p的地址
  34. SHW_ADR("b",b);//显示栈中数据b的地址
  35. SHW_ADR("nb",nb);//显示栈中数据nb的地址
  36. free(b);//释放申请的空间,以避免内存泄露
  37. free(nb);
  38. }
  39. void afunc(void)
  40. {
  41. static int level=0;//初始化为0的静态数据存储在BSS段中
  42. int stack_var;//局部变量,存储在栈区
  43. if(++level==5)
  44. return;
  45. SHW_ADR("stack_var in stack section",stack_var);
  46. SHW_ADR("leval in bss section",level);
  47. afunc();
  48. }
  49. /* Output
  50. etext address: 80488bf edata address: 8049b48 end address: 8049b58
  51. The main is at address: 80485be
  52. The afunc is at address: 8048550
  53. bss Locatoin:
  54. The bss_var is at address: 8049b54
  55. data Location:
  56. The data_var is at address: 8049b40
  57. Stack Loation:
  58. The stack_var in stack section is at address: ff9cdf80
  59. The level in bss section is at address: 8049b50
  60. The stack_var in stack section is at address: ff9cdf50
  61. The level in bss section is at address: 8049b50
  62. The stack_var in stack section is at address: ff9cdf20
  63. The level in bss section is at address: 8049b50
  64. The stack_var in stack section is at address: ff9cdef0
  65. The level in bss section is at address: 8049b50
  66. The string p in stack start is at address: ff9cdf70
  67. The string p in stack end is at address: ff9cdf90
  68. Heap Location:
  69. The allocated heap start is at address: 9020078
  70. The allocated heap end is at address: 90200c8
  71. p,b and nb in stack
  72. The p is at address: ff9cdfac
  73. The b is at address: ff9cdfa8
  74. The nb is at address: ff9cdfa4
  75. */

内存管理函数:
这里插入一段对void*的解释:
void*这不叫空指针,这叫无确切类型指针.这个指针指向一块内存,却没有告诉程序该用何种方式来解释这片内存.所以这种类型的指针不能直接进行取内容的操作.必须先转成别的类型的指针才可以把内容解释出来.

还有'\0',这也不是空指针所指的内容.'\0'是表示一个字符串的结尾而已,并不是NULL的意思.

真正的空指针是说,这个指针没有指向一块有意义的内存,比如说:
char* k;
这里这个k就叫空指针.我们并未让它指向任意内存.
又或者
char* k = NULL;
这里这个k也叫空指针,因为它指向NULL也就是0,注意是整数0,不是'\0'.

一个空指针我们也无法对它进行取内容操作.
空指针只有在真正指向了一块有意义的内存后,我们才能对它取内容.也就是说要这样
k = "hello world!";
这时k就不是空指针了.

void *malloc(size_t size)
(typedef unsigned int size_t;)
malloc在内存的动态存储区中分配一个长度为size字节的连续空间,其参数是无符号整型,返回一个指向所分配的连续空间的起始地址的指针。分配空间不成功(如内存不足)时返回一个NULL指针。

void free(void *ptr)
free释放掉内存空间。

void *realloc(void *ptr,size_tsize)
当需要扩大一块内存空间时,realloc试图直接从堆的当前内存段后面获得更多的内在空间,并返回原指针;如果空间不够就使用第一个能够满足这个要求的内存块,并将当前数据复制到新位置,释放原来的数据块;如果申请空间失败,返回NULL。

void *calloc(size_t nmemb, size_t size)
calloc是malloc的简单包装,它把动态分配的内存空间进行初始化,全部清0。此函数的实现描述:
void *calloc(size_t nmemb, size_t size)
{
void *p;

size_t total;
total=nmemb*size;

p=malloc(total);
if(p!=NULL)//申请空间

memset(p,'\0',total);//初始化\0

return p;
}

void *alloca(size_t size);
alloca在栈中分配size个内存空间(函数返回时自动释放掉空间,无需程序员手动释放),并将空间初始化为0。

转载自:https://blog.csdn.net/ywcpig/article/details/52303745

堆栈段、数据段、代码段相关推荐

  1. 段寄存器中代码段数据段堆栈段附加段

    段寄存器中代码段数据段堆栈段附加段

  2. 前端开发:微信小程序功能记录 2段 产品sku代码段

    前言: 学习永不止步 .... 成果图片以下,如果想简单,vant里面是有sku框架的,当时是因为不符合我所需要的,就自己纯手写了一个. 这个并非淘宝sku,把所有数据都便利出来的那种,这种是类型为主 ...

  3. VS2008中代码段(Snippet)及代码段编辑

    网上资源零碎,主要是整理下. 整理的是主要核心内容,完整内容可以看后面标注的原文链接. 代码段是什么 <VS2008中代码段(Snippet)的功能> 在VS2008中,提供了代码段(Sn ...

  4. linux进程的堆栈空间_代码段(指令,只读)、数据段(静态变量,全局变量)、堆栈段(局部变量)、栈【转】...

    转自:http://blog.csdn.net/gongweijiao/article/details/8207333 原文参见:http://blog.163.com/xychenbaihu@yea ...

  5. java数据段 静态区_linux进程的堆栈空间_代码段(指令,只读)、数据段(静态变量,全局变量)、堆栈段(局部变量)、栈【转】...

    一)概述 .堆栈是一个用户空间的内存区域,进程使用堆栈作为临时存储. .堆栈中存放的是函数中的局部变量,在函数的生命周期中可以将变量压入堆栈,编译器需要确保堆栈指针在函数退出前恢复到初始位置,也就是说 ...

  6. 代码段+数据段+bss段+stack+heap

    在学习之前我们先看看ELF文件. ELF分为三种类型:.o 可重定位文件(relocalble file),可执行文件以及共享库(shared library),三种格式基本上从结构上是一样的,只是具 ...

  7. 在代码段中安排自己定义的数据

    1.编程计算下面八个字型数据之和,将结果存放在ax寄存器中 1,2,3,4,5,6,7,8 通过前面的学习:将这一段数据安排在一段连续的内存中-->通过Loop指令,用add ax, ds:[b ...

  8. 贺利坚老师汇编课程28笔记:在代码段使用数据加个标号start

    指路老师的博客 编程计算以下8个数据的和,结果存放在AX寄存器里中 第一版代码 ASSUME CS:CODE CODE SEGMENT DW 0123H,0456H,0789H,0ABCH,0DEFH ...

  9. linux 查看进程数据段,如何读取Linux进程中的代码段和数据段

    Linux下的程序的文件格式是ELF,里面分了各种段,有代码段.数据段.等.当运行这个程序时,系统也会给这个进程创建虚拟内存,然后把ELF中的数据分别加载到内存中的对应位置.本文整理了用cpp程序读取 ...

  10. 特权级——保护模式的特权级检查 DPL,RPL,CPL, 一致代码段,非一致代码段

    特权级是保护模式下一个重要的概念,CPL,RPL和DPL是其中的核心概念,查阅资料无数,总结如下. 一.CPL.RPL.DPL简单解释     CPL是当前进程的权限级别(Current Privil ...

最新文章

  1. 窗口分析函数_1_生成不重复排名序号
  2. HDU5853 Jong Hyok and String(二分 + 后缀数组)
  3. 前端学习(2767):下拉刷新的学习
  4. 华为服务器型号命名,服务器的命名规则
  5. Spring从入门到入土——Bean的作用域与生命周期
  6. 改进后的新String类
  7. virtual关键字的使用(C# 参考)
  8. 【线程安全】—— 单例类双重检查加锁(double-checked locking)
  9. xcode checkout验证错误解决方法
  10. C++中使用初始化列表比在构造函数中对成员变量赋值更高效
  11. 流程图软件Visio的使用笔记
  12. Linux驱动开发: 杂项字符设备
  13. 《输赢》精彩段落总结
  14. 请按该计酬方式计算员工的工资。
  15. 全国首个数字产权区块链平台上线,共享购模式悄然上市
  16. 五险一金有哪些「知识普及」
  17. 开发一款简单的百度小程序(由微信小程序迁移过来)
  18. OPCODES学习网址
  19. 2021年华为云618年中钜惠,惊喜“惠”聚!
  20. uni-app 图片上传实战

热门文章

  1. IDEA 2019-1 激活码
  2. 精通Web Analytics 2.0 (6) 第四章:点击流分析的奇妙世界:实际的解决方案
  3. NVIDIA初创加速计划:12家获奖企业展示AI的无限可能
  4. Windows驱动开发入门系列教程
  5. linux trac git,trac装配、配置、中文化、支持git(Linux,Mac)
  6. Concave Function
  7. 最常用的9个机器学习算法,解决99%的业务问题!
  8. 常见计算机系统,计算机系统软件顶会OSDI 2021最佳论文出炉,邢波团队研究入选...
  9. 瑞昱RTL8710对标乐鑫ESP8266 谁将成为物联网首选
  10. 2014-2015英语学习总结——习惯了记录