为什么80%的码农都做不了架构师?>>>
11.1 为什么使用动态内存分配
使用数组有以下的缺点:
1. 使用数组引入了人为的限制,比如数组的大小实际上是确定的。
2. 如果确定了数组的大小,但实际上使用了较少的空间,则造成资源浪费。
3. 存在数组越界情况。
11.2 malloc和free
C函数库提供了两个函数,malloc和free,分别用来执行动态内存分配和释放。这些函数维护一个可用内存池。当一个程序另外需要一些内存时,它就调用malloc函数,malloc从内存池中提取一块合适的内存,并向该程序返回一个指向这块内存的指针。这块内存此时并没有以任何方式进行初始化。如果对这块内存初始化非常重要,要么手动进行初始化,要么使用calloc。当一块以前分配的内存不再使用时,程序调用free函数把它归还给内存池供以后之需。
函数原型如下:
void *malloc( size_t size );
void free( void *pointer );
malloc的参数就是需要分配的内存字节数,如果成功,则返回一个指向被分配的内存块起始位置的指针。
如果内存池是空的,或者它的可用内存无法满足你的请求,则malloc向操作系统请求,要求得到更多的内存,并在这块新的内存上执行分配任务。如果操作系统无法向malloc提供更多的内存,malloc返回一个NULL指针。因为,对每个从malloc返回的指针都进行检查,确保它并非是NULL是非常重要的。
free的参数必须要么是NULL,要么是一个先前从malloc,calloc或realloc返回的值。向free传递一个NULL参数不会产生任何的效果。
注意:malloc返回的是void*,所以请求的内存可以用于任何的数据类型。
11.3 calloc和realloc
void *calloc( size_t num_elements, size_t element_size );
void *realloc( void *ptr, size_t new_size );
calloc也用于分配内存,和malloc的主要区别是后者在返回指向内存的指针之前把它初始化为0.calloc的参数包括所需元素的数量和每个元素的字节数。根据这些值,它能够计算出总共需要分配的内存。
realloc函数用于修改一个原先已经分配的内存块的大小。使用这个函数,你可以使一块内存扩大或缩小。如果它用于扩大一个内存块,那么这块内存原先的内容依然保留,新增加的内存添加到原先内存块的后面,新内存并未以任何方法进行初始化。如果它用于缩小一个内存块,该内存块尾部的部分内存便被拿掉,甚于部分内存的原先内容依然保留。
如果原先的内存块无法改变大小,realloc将分配另一块正确大小的内存,并把原先那块内存的内容复制到新的块上。因此,在使用realloc之后,你就不能再使用指向旧内存的指针,而是应该改用realloc所返回的新指针。
最后,如果realloc函数的第一个参数是NULL,那么它的行为和malloc一模一样。
11.4 使用动态分配的内存
#include
#include
int main(void)
{
int *pi = malloc( 25 * sizeof( int ) );
int *tempPi = pi;
int i = 0;
for ( i = 0; i < 25; i++ ){
*pi++ = i;
}
pi = tempPi;
for ( i = 0; i < 25; i++ ){
printf("%d ", *pi++ );
}
free( pi );
printf("\nhello world\n");
return 0;
}
但是,这里在释放内存的时候卡住了,为什么?(即"hello world"并为被输出)。
原因很简单,因为pi已经进行了移位运算了,所以free(pi)指向了未知的内存位置。当然可以用free(tempPi)来释放内存,但是最好的方法是:通过tempPi完成赋值的操作,然后free(pi)。
11.5 常见的动态内存错误
在使用动态内存分配的程序中,常常会出现许多错误。这些错误包括对NULL指针进行解引用操作,对分配的内存进行操作时越过边界,释放并非动态分配的内存,试图释放一块动态分配的内存的一部分以及一块动态内存被释放之后将继续使用。
1. 动态内存分配最常见的错误就是忘记检查所请求的内存是否成功分配。
alloc.h:
#include
#define malloc
#define MALLOC(num, type) (type*)alloc( ( num ) * sizeof( type ) )
extern void *alloc( size_t size );
alloc.c:
#include
#include "alloc.h"
#undef malloc
void *alloc( size_t size )
{
void *new_mem;
new_mem = malloc( size );
if ( new_mem == NULL){
printf( "out of memory!\n" );
exit( 1 );
}
return new_mem;
}
main.c:
#include "alloc.h"
int main(void)
{
int *new_memory;
new_memory = MALLOC( 25, int );
free( new_memory );
return 0;
}
当然,释放一部分内存是错误的:
free( new_memory + 5 );
2. 动态内存分配的第二大错误来源是操作内存时超出了分配内存的边界。如果超出,则引起两种类型的问题:
1) 被访问的内存可能保持了其他变量的值,对它进行修改将破坏那个变量,修改那个变量将破坏存储在那里的值。
2) 在malloc和free的有些实现中,它们以链表的形式维护可用的内存池。对分配的内存之外的区域进行访问可能破坏这个链表,这有可能产生异常,从而终止程序。
3. 内存泄漏
当动态分配的内存不再需要使用时,它应该被释放,这样它以后可以被重新分配使用。如果不释放,则出现内存泄漏,导致可用内存越来越小,最终只能重启系统。
11.6 内存分配实例
1. 读取一列整数,并按升序排列它们,最后打印这个列表:
#include
#include
int compare_integers( void const *a, void const *b )
{
register int const *pa = a;
register int const *pb = b;
return *pa > *pb ? 1 : *pa < *pb ? -1 : 0;
}
int main(void)
{
int *array;
int n_values;
int i;
printf("how many values are there? ");
if ( scanf( "%d", &n_values ) != 1 || n_values <= 0 ){
printf("illegal number of values.\n");
exit( EXIT_FAILURE );
}
array = malloc( n_values * sizeof( int ) );
if (array == NULL ){
printf("can't get memory for that many values.\n");
exit( EXIT_FAILURE );
}
for ( i = 0; i < n_values; i++ ){
printf("?");
if ( scanf( "%d", array + i ) != 1 ){
printf("error reading value %d\n", i );
free(array);
exit( EXIT_FAILURE );
}
}
qsort( array, n_values, sizeof( int ), compare_integers );
for ( i = 0; i < n_values; i++ ){
printf("%d\n", array[i] );
}
free( array );
return EXIT_SUCCESS;
}
程序输出:
11.3 复制字符串
#include
#include
char *strdup( char const *string )
{
char *new_string;
new_string = malloc( strlen( string ) + 1 );
if (NULL != new_string ){
strcpy( new_string, string );
}
return new_string;
}
11.4 存货系统
inventor.h:
typedef struct{
int cost;
int supplier;
} Partinfo;
typedef struct {
int n_parts;
struct SUBASSYPART {
char partno[10];
short quan;
}*part;
} Subassyinfo;
typedef struct {
char partno[10];
int quan;
enum { PART, SUBASSY } type;
union {
Partinfo *part;
Subassyinfo *subassy;
}info;
}Invrec;
invcreat.c:
#include
#include
#include "inventor.h"
Invrec *create_subassy_record( int n_parts )
{
Invrec *new_rec;
new_rec = malloc( sizeof( Invrec ) );
if ( NULL != new_rec ){
new_rec->info.subassy = malloc( sizeof( Subassyinfo ) );
if ( NULL != new_rec->info.subassy ){
new_rec->info.subassy->part = malloc( n_parts * sizeof( struct SUBASSYPART ) );
if ( NULL != new_rec->info.subassy ){
new_rec->type = SUBASSY;
new_rec->info.subassy->n_parts = n_parts;
return new_rec;
}
free( new_rec->info.subassy );
}
free( new_rec );
}
return NULL;
}
invdelet.c:
#include
#include "inventor.h"
void discard_inventory_record( Invrec *record )
{
switch( record->type ){
case SUBASSY:
free( record->info.subassy->part );
free( record->info.subassy );
break;
case PART:
free( record->info.part );
break;
}
free( record );
}
习题:
1.
#include
#include
#include
void *my_calloc( size_t num_elements, size_t element_size )
{
void *new_mem;
new_mem = malloc( num_elements * element_size );
if ( NULL != new_mem ){
memset(new_mem, 0, num_elements * 4 / element_size );
return new_mem;
}
return NULL;
}
int main(void)
{
int *my_arr;
int i = 0;
int len = 0;
my_arr = my_calloc( 10, 2 );
len = 5;
for ( i = 0; i < len; i++ ){
printf("%d ", my_arr[i]);
}
return 0;
}
程序输出:
2.
#include
#include
int main(void)
{
int *arr;
int *temp;
int i;
int len = 0;
if ( scanf( "%d", &len ) != 1 ){
printf("error\n");
exit( EXIT_FAILURE );
}
arr = malloc( len * sizeof( int ) );
temp = arr;
while ( ( scanf("%d", &i) ) == 1 && i != EOF ){
*temp++ = i;
}
for ( i = 0; i < len; i++ ){
printf("%d ", arr[i]);
}
printf("\n");
free( arr );
return 0;
}
程序输出:
3.
#include
#include
#include
char *strcopy( char const *str ){
int len = strlen( str );
char *arr = malloc( len + 1 );
if ( NULL != arr ){
strcpy( arr, str );
}
return arr;
}
int main(void)
{
printf("%s\n", strcopy( "hello world" ) );
return 0;
}
程序输出:
4.
#include
#include
typedef struct node{
int value;
struct node *p_next;
}node;
int main(void)
{
node head;
node *node1 = malloc( sizeof( struct node ) );
node *node2 = malloc( sizeof( struct node ) );
node *node3 = malloc( sizeof( struct node ) );
node1->value = 5;
node1->p_next = node2;
node2->value = 10;
node2->p_next = node3;
node3->value = 15;
node3->p_next = NULL;
head = *node1;
printf("%d\n", head.p_next->p_next->value );
return 0;
}
程序输出: