关于内存思考之一:有效管理内存空间

来源:互联网 发布:光年seo日志分析工具 编辑:程序博客网 时间:2024/05/16 05:24

有效管理内存空间

在翻看《写给大家的C语言书》的时候,感觉里面写的内存管理比较不错,整理出来,再研读一遍。结合高质量c++编程指南

首选看什么是堆?

堆其实就是电脑中未使用的内存的集合。剩余的内存——在你的程序、程序的变量和操作系统的空间之后——组成了电脑上可用的堆空间。

如图:

  

DOS

   

你的C程序

   

你的变量

   

 

堆是内使用的内存

堆——连续内存中未被使用的部分

下面回顾下内存分配的方式:

(1)    在静态存储区分配:内存在程序编译的时候就已经分配好了,这块内存在程序的整个运行期间都存在。E.g.全局变量、static变量

(2)    在栈上创建:在执行函数的时候,函数内局部变量的存储单元都可以在栈上创建,函数执行结束的时候,这些存储单元自动内释放。在栈上创建,效率很高,但是内存空间有限。

(3)    由程序员自己在堆上分配,又称动态内存分配。程序在运行的时候用malloc或new申请任意多的内存,程序员自己负责在适当的时候用free或delete释放内存。

申请效率比较:

在静态存储区分配最快;

是由操作系统分配,速度较快,但程序员无法控制;

是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来比较方便;

在windows下,最好用VirtualAlloc分配内存,它不在堆,也不再栈,是直接在进程的地址空间中保留一块内存,虽然最不方便,但速度快也灵活。

堆的说明:

自由的堆内存称为自由堆,或未分配的堆内存。任何一次被程序使用过的堆称为已分配的堆。

使用堆

你将在程序运行时使用和释放堆内存,当你在请求堆内存的时候,你不能准确的知道新的内存从堆的什么地方来。因此,如果程序中的一条语句获取了堆内存后,然后正好下一条语句也获取另一部分堆内存,那么第二部分内存可能不会在物理上紧跟在第一部分内存的后面。

如果你一次分配了10个字节的堆内存,这10个字节将会是连续的。

一个指针指向所分配的堆的起始处。

使用堆内存,你就不必预先知道需要多少内存。堆使你的程序只使用所需的内存,当你的用户需要更多的内存时,程序可以分配内存。当用户使用完那些内存时,你可以释放分配的内存,使其可供其他需要内存的程序员使用。

分配堆:

使用malloc()函数分配对内存,使用free()函数释放分配堆的内存。

程序中要包含STFLIB.H头文件。

例如:分配10个整数来存放10个温度读数

首先你需要一个指向10个堆中的值的指针,int *temps;

下面是如何用malloc()在堆上分配10个整数:temps=(int*)malloc(10*sizeof(int));

如果是想分配10个字节,可以这样写:malloc(10);

malloc()函数总是执行下面2个步骤:

(1)    分配所请求的字节数并确保其他程序都不能改写这块内存直到程序将其释放;

(2)    把指针指向第1个分配的值。

malloc()函数总是返回一个字符型指针,所以要用(int*)进行强制类型转换。

堆内存分配失败:

在极端的情况下,可能没有足够的内存来满足malloc()函数的请求。

原因可能是用户的电脑没有很多内存,也可能是另一个程序已经使用了很多的内存,也可能是你的程序已经在前面分配了太多的内存。

如果malloc()分配内存失败了,它的指针变量将指向null值(0)。

因此,可以在malloc()语句后加上一条if语句:

temps=(int *)malloc(10*sizeof(int));

if(temps == 0)              或者用 if(!temps)

{

printf(“Oops! Not enough memory.\n”);

exit(1);

}

如果malloc()分配内存成功,temps会包含一个有效的地址,它指向所分配的堆的起始处。

如果malloc()分配内存失败了,temps就指向无效的地址0,并且在屏幕上打印出错误消息。

释放堆内存

用完堆内存后,还要把它还给系统,使用free()来实现。

例如要释放前面分配的10个整数,你可以向下面这样使用free()函数

free(10);

一旦释放就不能再把这些内存要回来。

记住:free()把已分配的内存扔回堆中,并且一旦扔回去了,这些内存就可能被另一程序夺回。

多次分配:

使用指针数组来分配许多不同组的对内存。

假如你在为本地的天气预报编写一个计算平均温度的程序。用户输入的温度读数越多,预报的结果就越精确。假设预报员要输入几个城市的历史温度读数。对每个不同的城市,预报员都有不同数量的读数。

指针用来处理这种问题。下面的语句说明了如何分配包含50个指针的数组:

int *temps[50];  这个数组存放的不是50个整数,而是50个指针,第一个指针叫做:temps[0],第二个叫做:temps[1],以此类推。

每个数组元素(每个指针)可以指向不同的已分配的堆内存集。

代码如下:

for(ctr=0;ctr<50;ctr++)

{

  puts(“howmany readings for the city?”);

  temps[ctr]=(int *)malloc(num*sizeof(int));

}

for(ctr=0;ctr<50;ctr++)

{

free(temps[ctr]);

}

总结:

1.     使用malloc()和free()函数来分配和释放内存;

2.     通过在malloc()的括号中使用sizeof()运算符来精确地告诉malloc()每次必须分配多大的内存;

3.     在函数的顶部只分配指针变量和其他变量。当你需要除了简单的循环计数器和总数之外的数据时,可以把数据放在堆中;

4.     如果必须跟踪几块堆内存,就使用指针数组。每个数组元素都可以指向不同数量的堆空间;

5.     检查以确保malloc()正确地工作。如果分配内存失败,malloc()就返回0.

6.     不要总是依赖普通数组来存放程序的数据。有时候,程序只在短时间才需要的数据,这种情况下使用堆能更好地利用内存资源。

原创粉丝点击