文档章节

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

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

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
粉丝 404
博文 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

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 ⋅ 0

C/C++内存分配问题

1、内存分配方式 内存分配方式有三种: (1)从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。 (2)在栈上创建。在...

我是一只鱼 ⋅ 2011/12/22 ⋅ 0

C Primer Plus 第12章 12.6 分配内存:malloc()和free()

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

idreamo ⋅ 2016/12/21 ⋅ 0

给自己记忆的const

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

Eysolitude ⋅ 2016/01/06 ⋅ 0

new/delete和malloc/free区别与联系

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

野渡书生 ⋅ 2016/04/05 ⋅ 0

c/c++内存机制(一)(原)

一:C语言中的内存机制 在C语言中,内存主要分为如下5个存储区: (1)栈(Stack):位于函数内的局部变量(包括函数实参),由编译器负责分配释放,函数结束,栈变量失效。 (2)堆(Heap):由...

xumaojun ⋅ 04/03 ⋅ 0

Cocos2d-x开发中C++内存管理

由于开始并没有介绍C++语言,C++的内存管理当然也没进行任何的说明,为了掌握Cocos2d-x中的内存管理机制,是有必要先了解一些C++内存管理的知识。 C++内存管理非常复杂,如果完全地系统地介绍...

智捷课堂 ⋅ 2014/11/02 ⋅ 0

C++学习笔记(四)

前言 本篇是《C++ Primer》的学习笔记,以及自己尝试实现数据结构Vector的部分功能,借此练习动态数组操作,同时介绍一种使用VS 2013进行内存泄露检测的方法。 动态数组 1、申请动态数组 默认...

初雪之音 ⋅ 2016/01/15 ⋅ 0

C语言内存管理(初级)----动态数组

C 语言提供的指针使我们可以直接操纵内存,在带来巨大灵活性的同时也带来了巨大的安全隐患。随着程序规模的增大,管理内存的难度也大为增加,内存管理应该说是一项艰巨任务。 C 语言引起的内...

zhcosin ⋅ 2012/12/07 ⋅ 4

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Python爬虫,抓取淘宝商品评论内容

作为一个资深吃货,网购各种零食是很频繁的,但是能否在浩瀚的商品库中找到合适的东西,就只能参考评论了!今天给大家分享用python做个抓取淘宝商品评论的小爬虫! 思路 我们就拿“德州扒鸡”...

python玩家 ⋅ 13分钟前 ⋅ 0

MySQL 内核深度优化

MYSQL数据库适用场景广泛,相较于Oracle、DB2性价比更高,Web网站、日志系统、数据仓库等场景都有MYSQL用武之地,但是也存在对于事务性支持不太好(MySQL 5.5版本开始默认引擎才是InnoDB事务...

java高级架构牛人 ⋅ 36分钟前 ⋅ 0

用户登录信息-钉子效果(基于jquery2.0)

本js效果使用jquery2.0,清晰的分解用户登录信息的(钉子效果),该效果直接用在作者网站(www.phpkhbd.com)上。 里面的难点有:定时器,延时。 大致效果如下: 一开始: 鼠标放上去的时候:...

宁哥实战课堂 ⋅ 37分钟前 ⋅ 0

解决yum安装报错Protected multilib versions

使用yum安装报错Protected multilib versions原因是因为多个库不能共存,不过更新的话也并不行,但是可以在安装命令后面加上如下一段命令: --setopt=protected_multilib=false 案例: 比如需...

北岩 ⋅ 48分钟前 ⋅ 0

为什么要学习Typescript???

简单来说 目前的typescript就是未来的javascript 为什么?? 这要从ECMA-262标准的第4版说起 对了 我们说的ES5 其实是ECMAScript3.1这个替代性建议被扶正了而已... 那么 第4版标准是什么? 看看...

hang1989 ⋅ 53分钟前 ⋅ 0

linux安装ipfs

一、下载ipfs # cd /usr/local/ipfs/ # wget https://dist.ipfs.io/go-ipfs/v0.4.15/go-ipfs_v0.4.15_linux-amd64.tar.gz # tar -zxvf go-ipfs_v0.4.15_linux-amd64.tar.gz 二、安装ipfs # ......

八戒八戒八戒 ⋅ 58分钟前 ⋅ 0

jvm程序执行慢诊断手册

生产环境最多的几种事故之一就是程序执行慢,如果是web服务的话,表现就是响应时间长。本文分享,从业多年形成的排查守则。 诊断步骤 系统资源查看 首先是系统资源查看,而且必须是在第一步。...

xpbob ⋅ 59分钟前 ⋅ 0

YII2 advanced 高级版本项目搭建-添加API应用以及多应用

一、YII安裝 安裝yii可以用composer安裝,也可以在yii中文社区下载归档文件安装 composer安装就不介绍了,因为要安装composer,比较麻烦,当然安装了composer是最好的,以后安装yii的插件要用...

botkenni ⋅ 59分钟前 ⋅ 0

在jdk1.8的环境下模拟永久代内存溢出

相信不少小伙伴在看深入理解Java虚拟机的时候,作者给我们举例一个demo来发生PermGen space 1、通过List不断添加String.intern(); 2、通过设置对应的-XX:PermSize与-XX:MaxPermSize(更快看到...

虾几把写 ⋅ 今天 ⋅ 0

开发OpenDaylight组件的完整流程

在前面介绍学习了OpenDaylight的几个重要模块后,这里再来介绍下完整开发一个模块的过程。 OSGI的bundles提供被其他OSGI组件调用的服务。这个教程中展示的是Data Packet Service去解析数据包...

wangxuwei ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部