|
技术交流 | 电路欣赏 | 工控天地 | 数字广电 | 通信技术 | 电源技术 | 测控之家 | EMC技术 | ARM技术 | EDA技术 | PCB技术 | 嵌入式系统 驱动编程 | 集成电路 | 器件替换 | 模拟技术 | 新手园地 | 单 片 机 | DSP技术 | MCU技术 | IC 设计 | IC 产业 | CAN-bus/DeviceNe |
大家来讨论一下动态分配内存问提 |
作者:db10 栏目:ARM技术 |
先看看一些基本概念:一、为什么用动态内存分配 但我们未学习链表的时候,如果要存储数量比较多的同类型或同结构的数据的时候,总是使用一个数组。比如说我们要存储一个班级学生的某科分数,总是定义一个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这样用为什么可以? |
2楼: | >>参与讨论 |
作者: computer00 于 2006/6/8 0:45:00 发布:
建议看看我的ARM_00_OS中的内存管理函数~~~~~~~ http://bbs.21ic.com/club/bbs/list.asp?boardid=35&t=2089994&tp=%u770b%u770barm%u83dc%u9e1f%u5728arm7%u4e0a%u5199%u7684%u64cd%u4f5c%u7cfb%u7edf%u2014%u2014arm%u5708%u5708%u64cd%u4f5c%u7cfb%u7edf /********************************************************************************************** 本程序只供学习使用,不得用于其它任何用途,否则后果自负。 ARM_00_OS_Core.c file 作者:Computer-lov 建立日期:2006-5-1 修改日期:2006-5-15 版本:V1.0 版权所有,盗版必究。 任何技术问题可到我的博客上留言: http://computer00.21ic.org COPYRIGHT(C) Computer-lov 2006-2016 All rights reserved **********************************************************************************************/ #include <ADuC7027.H> #include "interrupt.h" #include "LED.H" #include "ARM_00_OS_TaskSwitch.H" #include "my_type.h" #include "ARM_00_OS_Core.H" #include "UART.H" #include "KEYS.H" #include "Task.h" #define OSMemoryLack 0x0000000100000000 /*错误号:内存资源不足*/ /********************************************************************************************** 功能:内存管理。 入口参数1:Operation。操作方式。可以设置为MEMORY_ALLOCATION(分配)、MEMORY_FREE(释放)、统计使用量(MEMORY_STATISTIC) 入口参数2:StartAddr。起始地址,释放内存时使用。 入口参数3:Length。申请内存或释放内存时的长度,单位为字节。但实际分配时,是按块分配的,所以分配时, 实际分配到的数量可能会比指定的多,所以分配时,最好按块的整数倍大小来指定分配长度。 返回:32无符号型整数。 当操作为分配内存时,返回32位的内存首地址,返回0表示无足够多的可以用内存。 当操作为释放内存时,返回1表示释放成功。返回0表示释放出错。 当操作为统计内存使用量时,返回的是内存被使用的字节数。 当操作为获取缓冲池大小时,返回的是内存缓冲池大小。 备注:缓冲池大小由OSSizeOfMemoryPool指定。每块的大小由OSSizePerBlock指定 **********************************************************************************************/ uint32 OSMemoryManage(uint32 Operation,uint32 StartAddr,uint32 Length) { //内存分配表 //内存分配表是32位整数的一维数组。用每一位来表示一块是否被使用。当某位设置为1时,表示那一块被使用。 //当某位为0时,表示那一块可用。 static uint32 OSMemoryTable[OSSizeOfMemoryPool/OSSizePerBlock/32]; static uint32 OSMemoryPool[OSSizeOfMemoryPool/4]; //内存缓冲池。内存缓冲池为一个大是数组 uint32 BlankCount; //统计空块的计数器 uint32 Mask; //分配内存时用的掩码 volatile uint32 i,j; //循环用的变量 OSEnterCritical(); //进入临界段 SWITCH(Operation) //根据操作码,选择不同的操作 { case MEMORY_INIT: //如果是内存初始化 { for(i=0;i<OSSizeOfMemoryPool/OSSizePerBlock/32;i++) { OSMemoryTable[i]=0; //则将整张内存分配表清0 } OSExitCritical(); //退出临界段 return 1; //返回1 } case MEMORY_ALLOCATION: //如果是内存分配,则 { BlankCount=0; //先将内存空块的数量清0 for(i=0;i<(OSSizeOfMemoryPool/OSSizePerBlock/32);i++) //扫描整个内存分配表 { Mask=1; //掩码被设置为1,即最低位为1,其它位为0。 if(OSMemoryTable[i]==0xFFFFFFFF) //如果该字中的所以位都为1,表示该字节对应的所有块都被占用 { BlankCount=0; //空块计数器置0 continue; //退出本次循环,查找下一个字 } for(j=0;j<32;j++) //扫描一个字的32个bit是否有空闲的RAM { if((Mask & OSMemoryTable[i])==0) //如果该位为0,表示该块空闲 { BlankCount++; //空块计数器加1。 } else { BlankCount=0; //如果遇到非空块,则空块计数器置0。 } if((BlankCount*OSSizePerBlock)>=Length) //如果空闲的RAM,大于或者等于需要的长度,那么分配成功 { //计算被分配到的内存的起始地址,并将其保存在StartAddr中。 StartAddr=((uint32)OSMemoryPool)+(i*32+j+1)*OSSizePerBlock-OSSizePerBlock*BlankCount; while(1) //设置被使用的块为1 { OSMemoryTable[i] |=Mask; //将已经被分配的标志为1 Mask>>=1; //调整掩码的值 if(j==0) //如果已到最低位 { Mask=0x80000000; //则掩码调整为第31位为1 j=32; &nbs |
3楼: | >>参与讨论 |
作者: lpc2000 于 2006/6/8 3:57:00 发布:
在嵌入式环境里,malloc和free要自己写 不要去用现成的。 有的时候,malloc(0)回答1,还有free什么也不回,你怎么知道是不是free了呢?最好的是,写自己的malloc和free,自己定malloc的范围,自己来回挪指针。这样你就不用担心是malloc不对,还是free不对了。 例子的话,网上都有。比如, #define NULL 0 #define ALLOCSIZE 100 static CHAR allocbuf[ALLOCSIZE]; static CHAR *allocptr = allocbuf; CHAR *alloc(n) { if ( allocptr + n <= allocbuf + ALLOCSIZE) { allocptr += n; return( allocptr - n ); } else { return ( NULL ); } } free( p ) { if ( p >= allocbuf && p < allocbuf + BUFSIZE ) { allocptr = p; } } |
4楼: | >>参与讨论 |
作者: twentyone 于 2006/6/8 10:48:00 发布:
RE 楼上的说的对.大伙习惯用MALLOC和FREE是在WINDOWS/LINUX平台下养成的习惯.在WINDOWS/LINUX平台下,MALLOC和FREE是由操作系统提供支持的.如果你直接在板子上做开发,又要用MALLOC和FREE的话,需要自己实现,而且要注意分配好存储. |
5楼: | >>参与讨论 |
作者: slump 于 2006/6/8 12:33:00 发布:
ecos有完整c库支持 不用费心自己写了:) |
6楼: | >>参与讨论 |
作者: db10 于 2006/6/8 22:03:00 发布:
研究了你写的内存管理程序 感觉不错 准备在项目中用一用,感觉用自几写要放心点。 |
7楼: | >>参与讨论 |
作者: computer00 于 2006/6/8 23:44:00 发布:
当时写得太匆忙,刚看了一下,前面部分又做了一点点改动: /********************************************************************************************** 功能:内存管理。 入口参数1:Operation。操作方式。 可以设置为MEMORY_ALLOCATION(分配)、MEMORY_FREE(释放)、MEMORY_STATISTIC(统计使用量) MEMORY_INIT(初始化)、MEMORY_TEST(内存检测)、GET_MEMORY_POOL_SIZE(获取缓冲池大小) 入口参数2:StartAddr。起始地址,释放内存时使用。 入口参数3:Length。申请内存或释放内存时的长度,单位为字节。但实际分配时,是按块分配的,所以分配时, 实际分配到的数量可能会比指定的多,所以分配时,最好按块的整数倍大小来指定分配长度。 返回:32无符号型整数。 当操作为分配内存时,返回32位的内存首地址,返回0表示无足够多的可以用内存。 当操作为释放内存时,返回1表示释放成功。返回0表示释放出错。 当操作为统计内存使用量时,返回的是内存被使用的字节数。 当操作为获取缓冲池大小时,返回的是内存缓冲池大小。 备注:缓冲池大小由OSSizeOfMemoryPool指定。每块的大小由OSSizePerBlock指定 **********************************************************************************************/ uint32 OSMemoryManage(uint32 Operation,uint32 StartAddr,uint32 Length) { //内存分配表 //内存分配表是32位整数的一维数组。用每一位来表示一块是否被使用。当某位设置为1时,表示那一块被使用。 //当某位为0时,表示那一块可用。 static uint32 OSMemoryTable[OSSizeOfMemoryPool/OSSizePerBlock/32]; static uint32 OSMemoryPool[OSSizeOfMemoryPool/4]; //内存缓冲池。内存缓冲池为一个大是数组 uint32 BlankCount; //统计空块的计数器 uint32 Mask; //分配内存时用的掩码 volatile uint32 i,j; //循环用的变量 OSEnterCritical(); //进入临界段 SWITCH(Operation) //根据操作码,选择不同的操作 { case MEMORY_INIT: //如果是内存初始化 { for(i=0;i<OSSizeOfMemoryPool/OSSizePerBlock/32;i++) { OSMemoryTable[i]=0; //则将整张内存分配表清0 } for(i=0;i<OSSizeOfMemoryPool/4;i++) //内存缓冲池全部清0 { OSMemoryPool[i]=0; } OSExitCritical(); //退出临界段 return 1; //返回1 } case MEMORY_ALLOCATION: //如果是内存分配,则 { BlankCount=0; //先将内存空块的数量清0 for(i=0;i<(OSSizeOfMemoryPool/OSSizePerBlock/32);i++) //扫描整个内存分配表 { if(OSMemoryTable[i]==0xFFFFFFFF) //如果该字中的所以位都为1,表示该字节对应的所有块都被占用 { BlankCount=0; //空块计数器置0 continue; //退出本次循环,查找下一个字 } Mask=1; //掩码被设置为1,即最低位为1,其它位为0。 for(j=0;j<32;j++) //扫描一个字的32个bit是否有空闲的RAM { if((Mask & OSMemoryTable[i])==0) //如果该位为0,表示该块空闲 { BlankCount++; //空块计数器加1。 } else { BlankCount=0; //如果遇到非空块,则空块计数器置0。 } if((BlankCount*OSSizePerBlock)>=Length) //如果空闲的RAM,大于或者等于需要的长度,那么分配成功 { //计算被分配到的内存的起始地址,并将其保存在StartAddr中。 StartAddr=((uint32)OSMemoryPool)+(i*32+j+1)*OSSizePerBlock-OSSizePerBlock*BlankCount; while(1) //设置被使用的块为1 { OSMemoryTable[i] |=Mask; //将已经被分配的标志为1 Mask>>=1; //调整掩码的值 if(j==0) //如果已到最低位 { Mask=0x80000000; //则掩码调整为第31位为1 j=32; i--; //移到下一位 } BlankCount--; //空块计数减1 j--; //移到下一个字 if(BlankCount==0) //如果空块计数器减到0,则标志完毕 { OSExitCritical(); return StartAddr; //将启始地址StartAddr返回 } } &n |
8楼: | >>参与讨论 |
作者: db10 于 2006/6/9 0:39:00 发布:
刚才用了以下感觉还可以 你又改了?跟以前的比是拿部分改动了? |
9楼: | >>参与讨论 |
作者: computer00 于 2006/6/9 23:56:00 发布:
改了一点点^_^ 添加了缓冲池清0 for(i=0;i<OSSizeOfMemoryPool/4;i++) //内存缓冲池全部清0 { OSMemoryPool[i]=0; } if(OSMemoryTable[i]==0xFFFFFFFF) //如果该字中的所以位都为1,表示该字节对应的所有块都被占用 { BlankCount=0; //空块计数器置0 continue; //退出本次循环,查找下一个字 } Mask=1; //掩码被设置为1,即最低位为1,其它位为0。 以及把mask=1放到下面。 |
|
|
免费注册为维库电子开发网会员,参与电子工程师社区讨论,点此进入 |
Copyright © 1998-2006 www.dzsc.com 浙ICP证030469号 |