这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 综合技术 » 基础知识 » 讨论一下内存动态分配问题

共2条 1/1 1 跳转至

讨论一下内存动态分配问题

院士
2006-09-17 18:14:16     打赏
讨论一下内存动态分配问题



关键词: 讨论     一下     内存     动态     分配     问题    

院士
2006-12-22 22:43:00     打赏
2楼
问 先看看一些基本概念:一、为什么用动态内存分配

  但我们未学习链表的时候,如果要存储数量比较多的同类型或同结构的数据的时候,总是使用一个数组。比如说我们要存储一个班级学生的某科分数,总是定义一个float型(存在0.5分)数组:

float score[30];

  但是,在使用数组的时候,总有一个问题困扰着我们:数组应该有多大?

  在很多的情况下,你并不能确定要使用多大的数组,比如上例,你可能并不知道该班级的学生的人数,那么你就要把数组定义得足够大。这样,你的程序在运行时就申请了固定大小的你认为足够大的内存空间。即使你知道该班级的学生数,但是如果因为某种特殊原因人数有增加或者减少,你又必须重新去修改程序,扩大数组的存储范围。这种分配固定大小的内存分配方法称之为静态内存分配。但是这种内存分配的方法存在比较严重的缺陷,特别是处理某些问题时:在大多数情况下会浪费大量的内存空间,在少数情况下,当你定义的数组不够大时,可能引起下标越界错误,甚至导致严重后果。

  那么有没有其它的方法来解决这样的外呢体呢?有,那就是动态内存分配。

  所谓动态内存分配就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不象数组等静态内存分配方法那样需要预先分配存储空间,而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小。从以上动、静态内存分配比较可以知道动态内存分配相对于景泰内存分配的特点:

  1、不需要预先分配存储空间;

  2、分配的空间可以根据程序的需要扩大或缩小。

  二、如何实现动态内存分配及其管理

  要实现根据程序的需要动态分配存储空间,就必须用到以下几个函数

  1、malloc函数

  malloc函数的原型为:

void *malloc (unsigned int size)

  其作用是在内存的动态存储区中分配一个长度为size的连续空间。其参数是一个无符号整形数,返回值是一个指向所分配的连续存储域的起始地址的指针。还有一点必须注意的是,当函数未能成功分配存储空间(如内存不足)就会返回一个NULL指针。所以在调用该函数时应该检测返回值是否为NULL并执行相应的操作。

  下例是一个动态分配的程序:

#include
#include
main()
{
 int count,*array; /*count是一个计数器,array是一个整型指针,也可以理解为指向一个整型数组的首地址*/
 if((array(int *) malloc(10*sizeof(int)))==NULL)
 {
  printf("不能成功分配存储空间。");
  exit(1);
 }
 for (count=0;count〈10;count++) /*给数组赋值*/
  array[count]=count;
 for(count=0;count〈10;count++) /*打印数组元素*/
  printf("%2d",array[count]);
}

  上例中动态分配了10个整型存储区域,然后进行赋值并打印。例中if((array(int *) malloc(10*sizeof(int)))==NULL)语句可以分为以下几步:

  1)分配10个整型的连续存储空间,并返回一个指向其起始地址的整型指针

  2)把此整型指针地址赋给array

  3)检测返回值是否为NULL

  2、free函数

  由于内存区域总是有限的,不能不限制地分配下去,而且一个程序要尽量节省资源,所以当所分配的内存区域不用时,就要释放它,以便其它的变量或者程序使用。这时我们就要用到free函数。

  其函数原型是:

void free(void *p)

  作用是释放指针p所指向的内存区。

  其参数p必须是先前调用malloc函数或calloc函数(另一个动态分配存储区域的函数)时返回的指针。给free函数传递其它的值很可能造成死机或其它灾难性的后果。

  注意:这里重要的是指针的值,而不是用来申请动态内存的指针本身。例:

int *p1,*p2;
p1=malloc(10*sizeof(int));
p2=p1;
……
free(p2) /*或者free(p2)*/

  malloc返回值赋给p1,又把p1的值赋给p2,所以此时p1,p2都可作为free函数的参数。

  malloc函数是对存储区域进行分配的。

  free函数是释放已经不用的内存区域的。

  所以由这两个函数就可以实现对内存区域进行动态分配并进行简单的管理了。
另外一个规择:malloc与free要成对出现
  它们是一对恩爱夫妻,malloc少了free就必然会慢慢地死掉。成对出现不仅体现在有多少个malloc就应该有多少个free,还体现在它们应尽量出现在同一函数里,“谁申请,就由谁释放”,看下面的程序:
char * func(void)
{
char *p;
p = (char *)malloc(…);
if(p!=NULL)
…; /* 一系列针对p的操作 */
return p;
}
/*在某处调用func(),用完func中动态申请的内存后将其free*/
char *q = func();

free(q);
上述代码违反了malloc和free的“谁申请,就由谁释放”原则,代码的耦合度大,用户在调用func函数时需确切知道其内部细节!正确的做法是:
/* 在调用处申请内存,并传入func函数 */
char *p=malloc(…);
if(p!=NULL)
{
func(p);

free(p);
p=NULL;
}
/* 函数func则接收参数p */
void func(char *p)
{
… /* 一系列针对p的操作 */
}
free后一定要置指针为NULL,防止其成为“野”指针
我的使用:
在C51里(不带操作系统)我已经是可以做动态分配内存的。但是在51实现动态不像上面说的简单,开始也是根据上面说的做的,但是发现在51里这样做是不行的,后来终于实现了,那时必须在malloc之前调用一个init_mem(具体看KEIL库函数就知道)的函数分配一个比较大的空间之后,再用malloc就在这个已经分配的空间可以申请到内存了,就可以用malloc和free成对无限的使用这块大空间了。其实在51里说白了就是有限制的不是全自动的动态申请内存。

问题:现在我在lpc3132里(不带操作系统)这样用。当然不用象我51里一样init_mem
来先确定一个大范围,直接用malloc就可以申请到,并且连续申请500个10个字节大小的内存没有问题,当我用完他的一部分再释放free一部分。但是现在有个问题:我做了实验,当我连续申请了5K大小的空间,每次申请的大小是10个字节,即连续调用malloc达体是500次。我看到都是OK的,但是在然后的使用中,部分的用free 释放掉,当然其中可能又要使用到malloc,这时使用malloc就申请不到内存,但是我用了一个全局变量,即运行一次malloc是加一,运行一次free就减一,当不能申请时发现此时的这个全局变量值是才为2。但是为什么申请不到?这个全局变量值开始malloc次即是500可以的。而中间释放运行free,再malloc就不行,难道free不起作用,在51这样用为什么可以? 1: 如果无限制,也不用init_mem初始化,产生碎件怎么解决? 2: re楼上楼上说的对,关键在空间的碎片化。库函数不能给你分配连续的空间了,虽然空闲的空间还有很多。你后来要分配的空间比较大吧? 3: 做一个碎片整理的程序。当发现内存分配失败后,就执行下这个程序,整理下内存。

坏处:可能在整理内存的时候,不能相应任何操作。 4: re在使用数组的时候,总有一个问题困扰着我们:数组应该有多大?

类似地, 初始化内存时候, 可供malloc使用的堆内存空间应当有多大?

在OS中, 如果每个进程有自己独立的地址空间, 每个进程的堆空间应当有多大?

在OS中, 如果每个任务没有自己独立的地址空间, 系统的堆空间应当有多大?

一句话,这些信息都是由人为决定的,其依据都是估计的,至多是基于产品的一些指标要求或硬件限制。

楼主的文章中有意地避开了这些关键问题, 而重点分析malloc/free, 老实话,做为初学都理解malloc/free的基础教程可以。

工程上,碎片问题是动态内存管理的硬伤,在产品中,尤其是在对可靠性要求较高/很高的产品(如电信,航天产品)中几乎不用。
5: 如果你采用内存整理那是相当复杂的你必须要让应用程序知道你整理内存后实际数据的地址,因为他改变了,在pc上没有这个问题,因为内存很大,他可以在建立相应的管理结构,在嵌入式系统上正确的方法是采用内存链

共2条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]