C语言----动态内存

本文详细介绍了C语言中动态内存管理的函数,包括malloc、calloc、realloc和free的使用方法及注意事项。动态内存分配允许在运行时确定内存大小,但同时也可能导致内存错误,如NULL指针解引用、越界访问、释放非动态内存、部分释放内存、重复释放和内存泄漏。文章通过实例展示了这些错误,并提到了C99中的柔性数组特性,以及其在内存管理和访问速度上的优势。
摘要由CSDN通过智能技术生成

  1. 普通变量创建开辟空间大小是固定的
  2. 数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配(柔性数组除外,后文会写到

在内存中如图 所以就有了动态内存在这里插入图片描述

动态内存函数

1)malloc

void* malloc( size_t size );
size是分配未初始化储存的字节大小

  1. 开辟成功,则返回这个开辟的空间的指针
  2. 开辟失败,则返回NULL
  3. 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。
  4. 如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器
	int* ptr = NULL;
	ptr = (int*)malloc(num*sizeof(int));//注意malloc的强转类型
	if(NULL != ptr)//判断ptr指针是否为空
	{
		int i = 0;
		for(i=0; i<num; i++)
		{
			*(ptr+i) = 0}
	}
	free(ptr);//释放ptr所指向的动态内存
	ptr = NULL;//避免野指针

2)calloc

void* calloc( size_t num, size_t size );
num是数组元素个数 size是元素类型大小

  1. 函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0。
  2. 与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节全初始化为0
	int *p = calloc(10, sizeof(int));

3)realloc

void *realloc( void *ptr, size_t new_size );
ptr为前面malloc、calloc开辟空间的地址
size为新空间的大小(注意,是总大小不是新开辟的空间大小

  1. 返回值为调整之后的内存起始位置。
  2. 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到 新 的空间

realloc在调整内存空间的场景:

  1. 原有空间之后没有足够大的空间,函数会找一块够用的空间,同时把原来的数据copy下来,返回新开辟空间的地址,同时free掉原空间
  2. 原来的空间后面有足够的空间 紧接着直接开辟新空间
  3. 如果无空间可增加 则返回空指针,所以下面的代码是错误的
  4. 当没有空间时原来malloc开辟的空间的数据也会丢失
  5. 所以需要中间变量来确定realloc是否为空指针后再赋予p
//错误代码
p=realloc(...) //p为malloc开开辟的空间地址

4)free

void free( void* ptr );

  1. 如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
  2. 如果参数 ptr 是NULL指针,则函数什么事都不做
  3. free只能free堆区

动态内存常见错误

1)对NULL指针的解引用操作

注意:局部变量未初始化为随机值,不能解引用

//错误代码
void inside()
{
	int *p = (int *)malloc(INT_MAX/4);
	*p = 20;//当p为空指针,出现问题
)

2)对动态开辟空间的越界访问

逻辑错误

//错误代码
int *p = (int *)malloc(10*sizeof(int));
for(int i=0; i<=10; i++)
{
	*(p+i) = i;//当i是10的时候越界访问
}
free(p);

3)对非动态开辟内存使用free释放

int a = 10;栈区开辟空间

//错误代码
int a = 10;
int *p = &a;
free(p);//程序会崩溃

4)使用free释放一块动态开辟内存的一部分

p不再指向动态内存的起始位置

//错误代码
int *p = (int *)malloc(100);
p++;
free(p);//p++之前的空间将无法被释放

5)对同一块动态内存多次释放
详细剖析参考为什么重复free()比内存泄漏危害更大

解决办法:free()后将p置为空指针p=NULL;

//错误代码
int *p = (int *)malloc(100);
free(p);
free(p);//重复释放,程序崩溃

6)动态开辟内存忘记释放(内存泄漏)

忘记释放不再使用的动态开辟的空间会造成内存泄漏

//错误代码
int *p = (int *)malloc(100);
if(NULL != p)
{
	*p = 20;//没有free!!
}

>>四道题充分体会动态内存错误<<


C99柔性数组

在不同编译器中的两种写法

typedef struct flexible_array
{
	int i;
	int a[0];//柔性数组成员
}flexible_array;
typedef struct flexible_array
{
	int i;
	int a[];//柔性数组成员
}flexible_array;

为柔性数组开辟空间与使用

int i=0;
flexible_array *p = (flexible_array*)malloc(sizeof(flexible_array)+100*sizeof(int));
//如要继续开辟空间使用realloc即可
p->i = 100;
for(i=0; i<100; i++)
{
	p->a[i] = i;
}
free(p);

与结构体中的普通数组开辟空间比较

//普通数组
typedef struct normal_a
{
	int i;
	int *p_a;
}normal_a;
normal_a *p = malloc(sizeof(normal_a));
p->i = 100;
p->p_a = (int *)malloc(p->i*sizeof(int));
free(p->p_a);
p->p_a = NULL;
free(p);
p = NULL;

柔性数组优势

  1. 方便内存释放
  2. 有利于访问速度(连续的内存有益于提高访问速度,也有益于减少内存碎片

更深入理解 请参见陈皓大佬的>>C语言结构体里的成员数组和指针<<

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值