文档章节

C和指针---第十一章:动态内存分配

fzyz_sb
 fzyz_sb
发布于 2013/09/19 15:36
字数 2024
阅读 124
收藏 1

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 <stdio.h>
#include <stdlib.h>

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 <stdlib.h>

#define malloc
#define MALLOC(num, type) (type*)alloc( ( num ) * sizeof( type ) )
extern void *alloc( size_t size );
alloc.c:
#include <stdio.h>
#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 <stdlib.h>
#include <stdio.h>

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 <stdlib.h>
#include <string.h>

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 <stdlib.h>
#include <stdio.h>
#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 <stdlib.h>
#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 <stdio.h>
#include <stdlib.h>
#include <string.h>

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 <stdio.h>
#include <stdlib.h>

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 <stdio.h>
#include <string.h>
#include <stdlib.h>

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 <stdio.h>
#include <stdlib.h>

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;
}

程序输出:


© 著作权归作者所有

共有 人打赏支持
fzyz_sb
粉丝 408
博文 209
码字总数 447144
作品 0
武汉
程序员
C语言里用结构体和指针函数实现面向对象思想

转自:http://www.linuxidc.com/Linux/2016-12/138789.htm 一、基础研究 观察如下两个程序a.c和b.c: A.c: B.c: 这两个程序都是要实现在屏幕上第10行40列打印一个绿色的字符c: 这两个程序...

shenhuan1104
01/11
0
0
iOS开发小结(二):对象系统基础

Jason Lee @ 杭州 博客:http://blog.csdn.net/jasonblog 微博:http://weibo.com/jasonmblog [Outline] 1. id和Class 2. 动态地操作类 3. 实例化 [1. id和Class] ------------------------......

迷途d书童
2012/03/05
471
0
C Primer Plus 第12章 12.6 分配内存:malloc()和free()

首先,回顾一些有关内存分配的事实。所有的程序都必须留出足够内存来存储它们使用的数据。一些内存分配是自动完成的。例如,可以这样声明: float x; char place[]="Dancing oxen creek"; 于...

idreamo
2016/12/21
9
0
给自己记忆的const

参考:http://blog.csdn.net/EricJo/article/details/4138548 使用带有const的指针时其实有两种意思。一种指的是你不能修改指针本身的内容,另一种指的是你不能修改指针指向的内容 (1)指针本...

Eysolitude
2016/01/06
11
0
new/delete和malloc/free区别与联系

1、基本概念 malloc/free (1)、函数原型及说明 void malloc(long NumBytes): 该函数分配了NumBytes个字节,并返回了指向这块内存的指针。如果分配失败,则返回一个空指针NULL。 void fre...

野渡书生
2016/04/05
16
0

没有更多内容

加载失败,请刷新页面

加载更多

redis-hash

哈希类型是指健值本身又是一个键值对结构 基本命令: hset key field value 设置值 hget(获取),hdel(删除),hlen(计算field个数),hmget(批量设置),hexists(是否存在),hkeys(获取所有的...

拐美人
17分钟前
1
0
简单的svm例子

数据来源:https://github.com/oumiga1314/Coursera-ML-AndrewNg-Notes/blob/master/code/ex6-SVM/data/ex6data1.mat import pandas as pd import numpy as np import scipy.io as sio impor......

南桥北木
21分钟前
0
0
android 关于View的一些整理

1、Button text的值为英文时,会自动转换成大写。如需取消,设置android:textAllCaps="false" 2、控件的可见性 可以在layout的配置文件中,配置android:visibility属性 调用setVisibility()...

西米小娅
31分钟前
0
0
Spring JDBC数据源分析

Spring数据源分析 分析这样一段代码: package com.jason.spring.datasource.jdbc;import org.springframework.context.support.ClassPathXmlApplicationContext;import org.springframew......

宸明
39分钟前
1
0
FatJar:适用于sdk多module打包和合并多个jar的gradle插件

usage: 1.下载fatJar.gradle放置于project根目录 2.在project的build.gradle中添加依赖和配置: apply from: 'fatJar.gradle'buildscript { dependencies { classpath 'xyz......

SuShine
56分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部