找回密码
 用户注册

QQ登录

只需一步,快速开始

查看: 3904|回复: 0

[原]动态内存分配

[复制链接]
发表于 2012-2-4 22:03:48 | 显示全部楼层 |阅读模式
前言
1.数组的元素存储于内存中连续的位置上。当一个数组被声明时,它所需要的内存在编译时就被分配。
2.但是我们也可以使用动态内存分配在运行时为它分配内存。
3.为什么使用动态内存分配
1>当使用数组时,必须用一个常量来指定数组的长度。但是,有时候,数组的长度常常在运行时才知道。因此,在某些情况下,我们通常采取声明一个较大的数组,它可以容纳可能出现的最多元素。
2>该方法的优点是:简单。
3>它的缺点是:
Ø  这种声明在程序中引入了人为的限制,如果程序需要使用的元素数量超过了声明的长度,它就无法处理这种情况。要避免这种情况,最简单的方法就是把数组声明的更大一些。
Ø  如果程序实际需要的元素数量比较少时,巨型数组的绝大部分内存空间都被浪费了。
Ø  如果输入的数据超过了数组的容纳范围时,程序必须以一种合理的方式作出响应。
一.malloc和free
1.c函数库提供了两个函数,malloc和free,分别用于执行动态内存分配和释放。这些函数维护一个可用内存。
2.当一个程序另外需要一些内存时,它就调用malloc函数,malloc从内存池中提取一块合适的内存。并向该程序返回一个指向这块内存的指针。这块内存此时并没有以任何方式进行初始化。如果要对其进行初始化,要么自己动手进行初始化,要么使用calloc()函数。当一块以前分配的内存不在使用时,程序调用free函数把它归还给内存池供以后使用。
3.这两个函数的原型如下,都在头文件stdlib.h中声明
1>void *malloc(size_t  size);
2>void   free(void  *pointer)
4.malloc的参数就是需要分配的内存字节数。如果内存池中的可用内存可以满足这个需求,malloc就返回一个指向被分配的内存块起始位置的指针。
1>malloc所分配的是一块连续的内存。例如:如果请求分配100个字节的内存,那么它实际分配的内存就是100个连续的字节,并不会分开位于两块或多块不同的内存。同时,malloc实际分配的内存有可能比你请求的稍微多一点。但是这时由编译器定义的。
2>如果内存池是空的,或者它的可用内存无法满足要求时。在该情况下,malloc()函数向操作系统请求,要求得到更多地内存,并在这块内存上执行分配任务。如果操作系统无法向malloc提供更多的内存,malloc就返回一个NULL指针。因此,要对每个从malloc返回的指针都进行检查,确保它并非NULL是非常重要的。
5.free的参数必须要么是NULL,要么是一个先前从malloc,calloc或realloc返回的数值。想free传递一个NULL参数不会产生任何效果。
6.malloc并不知道请求的内存需要存储的是整型、浮点值、结构还是数组。Malloc返回一个类型为void *的指针。在标准中表示一个void *类型的指针可以转换为其他任何类型的指针。因此,在使用前,要进行强制类型转换。
二.calloc和realloc
1.还有两个内存分配函数,calloc和realloc。它们的原型如下:
void   *calloc(size_t num_elements,size_t element_size);
void   realloc(void  *ptr,size_t  new_size);
2.calloc也用于分配内存。malloc和calloc之间的区别是后者在返回指向内存的指针之前把它初始化为0。但如果程序只是想把一些值存储到数组中,那么这个初始化过程纯属浪费时间。
3.calloc和malloc之间另一个较小的区别是它们请求内存数量的方式不同。calloc的参数包括所需元素的数量和每个元素的字节数。根据这些值,它能够计算出总共需要分配的内存。
4.realloc函数用于修改一个原先已经分配的内存块大小。
1>使用这个函数,你可以使一块内存扩大或缩小。如果它用于扩大一个内存块,那么这块内存原先的内容依然保留,新增加的内存添加到原先内存块的后面,新内存并未以任何方法进行初始化。
2>如果它用于缩小一块内存块,该内存块尾部的部分内存便被拿掉,剩余部分内存的原先内容依然保留。
3>如果原先的内存块无法改变大小,realloc将分配另一块正确大小的内存,并把原先那块内存的内容复制到新的块上。因此,在使用realloc之后,就不能再使用指向旧内存的指针,而是应该该用realloc所返回的新指针。
4>如果realloc函数的第一个参数是NULL,那么它的行为就和malloc一模一样。
三.使用动态内存分配实例
1.使用malloc分配一块内存
int  *pi;
….
pi = malloc(100);
if (pi  ==  NULL){
           printf(“Outof  memory!\n”);
           exit(1);
}
符号NULL定义于stdio.h,它实际上是字符值常量0。
1>如果内存分配成功,那么得到了一个指向100个字节的指针。在整型为4个字节的机器上,这块内存将被当作25个整型元素的数组,因为pi是一个指向整型的指针。
2>但是,使用上面的程序可移植性较差:
可以使用如下的方法:
pi  =  malloc(25 *sizeof(int));
使用该方法,即使在整数长度不同的机器上,它也能获得正确的结果。
3>已经有了指针,,如何使用这块内存呢。可以使用间接访问和指针运算来访问数组的不同整数位置,下面通过循环来给新分配的数组的每个元素都初始化为0.
int  *pi2,i;
……
pi2 =  pi;
for(i = 0;i <25;i++)
*pi2++=0;
我们也可以使用小标来运算。
  int  i;
  …..
  for( I=0;i<25;i++)
pi = 0;
四.常见的动态内存错误
1.使用动态内存分配的程序中,常常会出现很多错误。
1>对NULL指针进行解引用操作
2>对分配的内存进行操作时越过边界
3>释放并非动态分配的内存
4>试图释放一块动态分配的内存的一部分以及一块内存被释放之后被继续使用。
说明:
² 动态分配最常见的错误就是忘记检查所请求的内存是否成功分配。
² 2.动态内存分配的第二大错误来源是操作内存时超出了分配内存的边界。
2.当你使用free时,可能出现各种不同的错误。
1>传递给free的指针必须是一个从malloc、calloc或realloc函数返回的指针。
2>传递给free函数一个指针,让它释放一块并非动态分配的内存可能导致程序立即终止或在晚些时候终止。
3>试图释放一块动态分配内存的一部分也有可能引起类似问题。
Eg:
/**
***Get 10 integers
**/
pi  = malloc(10*sizeof(int ));
….
/*
**仅释放后5个整数,前面的5个数不释放
*/
free(pi + 5);
说明:
² 释放一块内存的一部分是不允许的。动态分配的内存必须整块一起释放。但是,realloc函数可以缩小一块动态分配的内存,有效地释放它尾部的部分内存。
4>不要访问已经被free函数释放了的内存。假定对一个指向动态分配的内存的指针进行了复制,而且这个指针的几份拷贝分散于程序各处。你无法保证当你使用其中一个指针时它所指向的内存是不是已被另一个指针释放。还要确保程序中所有使用这块内存的地方在这块内存释放之前停止对它的使用。
5>当动态分配的内存不再需要使用时,应该被释放,这样可以被重新分配使用。分配内存但在使用完毕后不释放将引起内存泄漏(memory leak)。
总结:
1.当数组被声明时,必须在编译时知道它的长度。动态内存分配允许程序为一个长度在运行时才知道的数组分配内存空间。
2.malloc和calloc函数都用于动态分配一块内存,并返回一个指定该块内存的指针。
1>malloc的参数就是需要分配的内存的字节数。
2>calloc的参数是需要分配的元素个数和每个元素的长度。calloc函数在返回前把内存初始化为零。malloc函数返回时内存并未以任何方式进行初始化。
3>调用realloc函数可以改变一块已经动态分配的内存的大小。增加内存块大小有时有可能采取的方法是把原来内存块上的所有数据复制到一个新的、更大的内存块上。当一个动态分配的内存块不再使用时,应该调用free函数把它归还给可用内存池,内存释放后便不能再被访问。
3.如果请求的内存分配失败,malloc、malloc和readlloc函数返回的将是一个NULL指针。
4.错误的访问分配内存之外的区域所引起的后果类似越界访问一个数组,但这个错误还能破坏可用内存池,导致程序失败。
5.如果一个指针不是从早先的malloc、calloc或realloc函数返回的,它是不能作为参数传递给free函数的。
作者:tigerjb 发表于2012-2-1 19:56:40 原文链接

您需要登录后才可以回帖 登录 | 用户注册

本版积分规则

Archiver|手机版|小黑屋|ACE Developer ( 京ICP备06055248号 )

GMT+8, 2024-5-1 03:15 , Processed in 0.015497 second(s), 6 queries , Redis On.

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表