【C/C++值班室】,程序运行时的内存使用(一)(大家来拍砖啊)

北极猩猩 2004-07-10 11:44:09
【C/C++值班室】,程序运行时的内存使用(一)

计算机程序运行时需要保存和处理数据,而所有的运行时使用的数据都是保存在内存当中的。下面我们就来看一看程序在运行时是如何使用内存的。

一个程序中使用的内存都是通过变量来访问的(可能是直接访问,也可能是间接访问),我们看看一个程序中都有哪些种类的变量,它决定着程序运行时的内存使用方式。

类型一:全局变量、静态变量、静态成员变量和静态局部变量
这些变量的特点是它们从程序运行的一开始就存在着,可以在程序运行过程中的任意时刻,从任意的位置进行访问,它们将一直生存到程序结束时为止。
这一类变量是在一块固定的全局内存区(或称为静态内存区)中分配的,这块空间由程序在开始运行时保留,保存在这一空间中的变量也在运行开始是创建并初始化。所有这些变量都一直保存在这块区域中,并在整个程序的运行过程中都是存在的。因此,程序可以随时访问。

类型二:局部变量
局部变量指那些在函数中声明和使用的变量,它们伴随着函数的执行而生,伴随着函数的返回而死,它们的生存历程就是所在函数的执行过程。它们只能在所在的函数中访问,在函数之外没有意义。它们的特点是,生存时间是完全确定的,生命周期由函数调用机制处理。
这类变量通常在一块称为栈(stack)的内存空间中分配。stack所需要的空间通常是在程序运行开始是预先分配的,但是,占空间的使用并不是固定的。在我们调用一个函数时,程序会为我们在栈上面分配一块这个函数需要的内存空间,这块空间称为栈帧(stack frame),被调函数所需要的所有局部变量都在函数开始执行之前在这块空间上创建,在函数执行结束时,栈帧将被撤销,函数中的局部变量就不再存在了。(所以,如果函数返回一个执行局部变量的指针,该指针指向的内存在被访问时会出现非法内存访问错误)栈中的数据可以在当前函数和所有被当前函数调用的函数中使用,因为在函数调用其他函数时不会撤销自己的栈帧,而是在自身栈帧之上创建为被调函数创建一个新的栈帧。
要注意,程序预先分配的栈空间是有限的(这个值可以在编译时确定),因此,程序的函数调用就不会是无止境的,如果函数调用过深,或者函数局部变量过多(比如:int a[100000000000000] :P)将会导致程序耗尽栈内空间而报错(stack overflow)。

类型三:动态分配的内存
动态分配的内存是指那些用malloc(realloc,...)和new、new[]分配的内存,它们在程序中是不能够直接访问的,必须通过指针,用*p或者p[10]再或者p->i的方式间接访问,而它们的释放必须由程序员通过free或delete、delete[]主动进行(除非你的语言的runtime可以向java那样自动进行内存回收),它们的生存周期可以由程序员随心所欲的控制,要长即长,说短就短。不过也可能由于程序员忘记回收而使无用的数据一直占据着内存。
这类变量在另一块内存中分配,这块内存称为堆(heap)。堆中的内存可以根据实际需要在程序的运行过程中分配(malloc/new)和释放(free/delete),在堆中分配的数据将一直存在下去,直到明确地被释放或者保存到程序运行结束时。堆中的内存被释放之前它可以在程序中任意访问,并被所有函数共享访问(注意和战中变量的区别)。堆中内存的分配通常是由操作系统的API进行的(比如windows中的GlobalAlloc等),而C/C++中使用malloc和new来调用API进行内存分配。
动态内存分配永远是同指针联系在一起的,因为对中分配的内存不能直接在程序中访问,必须通过指针变量进行间接访问。对堆中内存的分配操作将会返回分配好的内存的一个指针,我们通过这个值针对分配的空间进行操作。指针是一个变量,它保存着一个内存地址,使我们可以根据这个地址访问这个地址单元上所保存的数据。


程序对内存的动态分配能力是程序设计灵活性的一个象征(要知道,最早的高级语言入Fortan是不能动态分配内存的)。而对于是用指针通过手动内存管理的C/C++来说,灵活意味着风险。指针的使用可能会缠身很多错误,现在让我们一一道来。
1,非法访问
指针中保存一块内存单元的地址,可是它却不能保证这块地址一定是我们可以访问的。这块地址可能是一块不属于你的内存,如果,你强行访问那么轻则产生保护性错误,使你的程序崩溃;重则破坏系统的数据结构是操作系统崩溃。

2,访问越界
我们可以使用指针分配一整块内存供程序使用,可以程序并不会监督你的访问范围是不是超出了你分配的大小,一切都要你自己负责。比如:
char * s= new char[100];
*(s+100) = 'h'; //Boom!!!!!

3,内存泄漏
我们一旦在堆中分配了内存他就会一直存在,直到我们释放。而如果我们由于某种原因,在释放之前就失去了对所分配的内存的指针,那么这块内存将变成一只断了线的风筝,永远占据着内存空间。而如果这种丢失经常而且持续性的发生,我们的程序就会像漏水的桶一样,最终让无用数据耗光所有的内存空间。下面给出一个例子,看看大家能不能知道内存是怎样溜掉的。

bool m() {
try{
Something * s=new Something();
//do something...
delete s;
}
catch(int errCode) {
return false;
}
return true;
}

4,错误释放
和内存泄漏相对,太过积极的释放内存也会产生错误。首先,不是任意的内存都是可以释放的,必须要保证指针指向的是由malloc/new分配的内存,而且指向的分配到的内存的开始位置,否则结果就和非法访问一样。同样,对已经是放过的内存再次调用free和delete也不会得到什么好下场。
由于,C++中规定delete 0不会产生任何错误,所以,在每次调用delete之后将指针设为0是一个不错的习惯,值得发扬。



本文简单介绍了应用程序中对内存分配和使用的知识。在后边的文章里我们将继续讨论,内存分配对程序设计带来的影响,欢迎大家拍砖(最好用金砖)

【C/C++值班室|北极星|2004.7.10】
...全文
1097 46 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
46 条回复
切换为时间正序
请发表友善的回复…
发表回复
yangfasheng 2004-11-03
  • 打赏
  • 举报
回复
MARK
beyondtkl 2004-07-13
  • 打赏
  • 举报
回复
mk
真的李小龙 2004-07-12
  • 打赏
  • 举报
回复
楼不在高,有仙则灵。
不就是写‘程序开发人员’的恶作句。
要想搞懂:抛弃WINDOWS
用LINUX。不过要先看他的原代码哦!
我真准备看:我机子上有!
howler 2004-07-12
  • 打赏
  • 举报
回复
mark
BluntBlade 2004-07-12
  • 打赏
  • 举报
回复
我写的专题没人理……
:....(
hxygx 2004-07-12
  • 打赏
  • 举报
回复
好标记
niky8053 2004-07-12
  • 打赏
  • 举报
回复
非常感谢,C/C++值班室
lbaby 2004-07-11
  • 打赏
  • 举报
回复
!@@#!@$(&$%(^$~
没砖了,你等着啊~~
aixuer 2004-07-11
  • 打赏
  • 举报
回复
mark & up
oldjackyone 2004-07-11
  • 打赏
  • 举报
回复
估计是废人干的,嘿嘿~~
  • 打赏
  • 举报
回复
呵,不是我干的:)
北极猩猩 2004-07-11
  • 打赏
  • 举报
回复
居然被置顶了,头一回享受这么高的荣誉
北极猩猩 2004-07-11
  • 打赏
  • 举报
回复
再顶一顶,快来人看啊。
haha52 2004-07-11
  • 打赏
  • 举报
回复
UP
Wolf0403 2004-07-11
  • 打赏
  • 举报
回复
http://community.csdn.net/Expert/TopicView1.asp?id=3162763

狗狗的问题我没法解答,希望北极星兄台能指点迷津
Jinhao 2004-07-10
  • 打赏
  • 举报
回复
不错,辛苦了,我顶
期待后边地
freefalcon 2004-07-10
  • 打赏
  • 举报
回复
up,不错,辛苦了
补充一点,全局变量的初始化顺序是不被编译器所保证的
北极猩猩 2004-07-10
  • 打赏
  • 举报
回复
yesdo(小兵)
呵呵,那本书我没看过。

这是本人系列文章的第一部分,后边还有哦
peter9606 2004-07-10
  • 打赏
  • 举报
回复
期待着 二 的早日面世 ;)
peter9606 2004-07-10
  • 打赏
  • 举报
回复
4,错误释放
和内存泄漏相对,太过积极的释放内存也会产生错误。首先,不是任意的内存都是可以释放的,必须要保证指针指向的是由malloc/new分配的内存,而且指向的分配到的内存的开始位置,否则结果就和非法访问一样。同样,对已经是放过的内存再次调用free和delete也不会得到什么好下场。
由于,C++中规定delete 0不会产生任何错误,所以,在每次调用delete之后将指针设为0是一个不错的习惯,值得发扬。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~学习
加载更多回复(25)
1. C 语言中的指针和内存泄漏 5 2. C语言难点分析整理 10 3. C语言难点 18 4. C/C++实现冒泡排序算法 32 5. C++中指针和引用的区别 35 6. const char*, char const*, char*const的区别 36 7. C中可变参数函数实现 38 8. C程序内存中组成部分 41 9. C编程拾粹 42 10. C语言中实现数组的动态增长 44 11. C语言中的位运算 46 12. 浮点数的存储格式: 50 13. 位域 58 14. C语言函数二维数组传递方法 64 15. C语言复杂表达式的执行步骤 66 16. C语言字符串函数大全 68 17. C语言宏定义技巧 89 18. C语言实现动态数组 100 19. C语言笔试-运算符和表达式 104 20. C语言编程准则之稳定篇 107 21. C语言编程常见问题分析 108 22. C语言编程易犯毛病集合 112 23. C语言缺陷与陷阱(笔记) 119 24. C语言防止缓冲区溢出方法 126 25. C语言高效编程秘籍 128 26. C运算符优先级口诀 133 27. do/while(0)的妙用 134 28. exit()和return()的区别 140 29. exit子程序终止函数与return的差别 141 30. extern与static存储空间矛盾 145 31. PC-Lint与C\C++代码质量 147 32. spirntf函数使用大全 158 33. 二叉树的数据结构 167 34. 位运算应用口诀和实例 170 35. 内存对齐与ANSI C中struct内存布局 173 36. 冒泡和选择排序实现 180 37. 函数指针数组与返回数组指针的函数 186 38. 右左法则- 复杂指针解析 189 39. 回车和换行的区别 192 40. 堆和堆栈的区别 194 41. 堆和堆栈的区别 198 42. 如何写出专业的C头文件 202 43. 打造最快的Hash表 207 44. 指针与数组学习笔记 222 45. 数组不是指针 224 46. 标准C中字符串分割的方法 228 47. 汉诺塔源码 231 48. 洗牌算法 234 49. 深入理解C语言指针的奥秘 236 50. 游戏外挂的编写原理 254 51. 程序实例分析-为什么会陷入死循环 258 52. 空指针究竟指向了内存的哪个地方 260 53. 算术表达式的计算 265 54. 结构体对齐的具体含义 269 55. 连连看AI算法 274 56. 连连看寻路算法的思路 283 57. 重新认识:指向函数的指针 288 58. 链表的源码 291 59. 高质量的子程序 295 60. 高级C语言程序员测试必过的十六道最佳题目+答案详解 297 61. C语言常见错误 320 62. 超强的指针学习笔记 325 63. 程序员之路──关于代码风格 343 64. 指针、结构体、联合体的安全规范 346 65. C指针讲解 352 66. 关于指向指针的指针 368 67. C/C++ 误区一:void main() 373 68. C/C++ 误区二:fflush(stdin) 376 69. C/C++ 误区三:强制转换 malloc() 的返回值 380 70. C/C++ 误区四:char c = getchar(); 381 71. C/C++ 误区五:检查 new 的返回值 383 72. C 是 C++ 的子集吗? 384 73. C和C++的区别是什么? 387 74. 无条件循环 388 75. 产生随机数的方法 389 76. 顺序表及其操作 390 77. 单链表的实现及其操作 391 78. 双向链表 395 79. 程序员数据结构笔记 399 80. Hashtable和HashMap的区别 408 81. hash 表学习笔记 410 82. C程序设计常用算法源代码 412 83. C语言有头结点链表的经典实现 419 84. C语言惠通面试题 428 85. C语言常用宏定义 450

65,187

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧