动态内存管理(C语言版)

通过创建变量或数组可以开辟内存,然而像这样的开辟方法所开辟的内存大小是固定的,一经创建,其大小无法改变,像这样的内存属于静态内存;相对于静态内存,也存在有动态内存,顾名思义,其内存大小可以根据需要进行动态的增长或缩小。


动态内存的开辟

C语言中可以通过malloc函数与calloc函数开辟动态内存,二者作用类似,功能略不相同。

动态内存使用完毕后需要释放,不然会造成内存泄漏,内存泄漏到一定程度,可能导致程序崩溃,因此每次使用完动态内存都需要进行释放,而C语言中专门用来释放动态内存申请的空间函数为free函数。

所有的动态内存相关函数均定义于<stdlib.h>的头文件中。

malloc函数

定义

void* malloc( size_t size );

功能:分配 size 字节的未初始化内存。
若分配成功,则返回为任何拥有基础对齐的对象类型对齐的指针。
若 size 为零,则 malloc 的行为视编译器而定。

参数
size - 要分配的字节数
返回值
成功时,返回指向新分配内存的指针。为避免内存泄漏,必须用 free() 或 realloc() 解分配返回的指针。
失败时,返回空指针。

举例:

#include<stdlib.h>
int main()
{
	int* p1 = (int*)malloc(3 * sizeof(int));//分配最多可以容纳三个整型的空间
	char* p2 = (char*)malloc(10 * sizeof(char));//分配10个字节的空间

	//p1与以下等价,均分配12个字节的空间
	int* p3 = malloc(3 * sizeof(int));
	int* p4 = malloc(sizeof(int[3]));
	int* p5 = malloc(3 * sizeof * p5);

	//使用完后释放
	free(p1);
	free(p2);
	free(p3);
	free(p4);
	free(p5);
	p1 = NULL;
	p2 = NULL;
	p3 = NULL;
	p4 = NULL;
	p5 = NULL;
	return 0;
}

使用free函数释放掉内存后,要及时将指针置空,否则原指针成为野指针

calloc函数

定义:

void* calloc( size_t num, size_t size );

功能:

为 num 个对象的数组分配内存,并初始化所有分配存储中的字节为零。
若分配成功,会返回指向分配内存块最低位(首位)字节的指针,它为任何类型适当地对齐。
若 size 为零,行为视编译器而定。

参数
num - 对象数目
size - 每个对象的大小
返回值
成功时,返回指向新分配内存的指针。为避免内存泄漏,必须用 free() 或 realloc() 解分配返回的指针。
失败时,返回空指针。

可以看到malloc函数与calloc函数的作用是相同的,不过calloc函数直接初始化了开辟的内存空间,而malloc函数需要另外再写代码初始化(如果需要的话);此外,二者的参数部分不相同,malloc函数直接开辟一定字节的空间,而calloc函数则需要告知变量类型的大小和数目。

realloc()函数

定义:

void *realloc( void *ptr, size_t new_size );

功能:

重新分配给定的内存区域。它必须是之前为 malloc() 、 calloc() 或 realloc() 所分配,并且仍未被 free 或 realloc 的调用所释放。否则,结果未定义。

参数
ptr - 指向需要重新分配的内存区域的指针
new_size - 数组的新大小(字节数)
返回值
成功时,返回指向新分配内存的指针。返回的指针必须用 free() 或 realloc() 释放。原指针 ptr 被释放。
失败时,返回空指针。原指针 ptr 保持有效,并需要通过 free() 或 realloc() 释放。


realloc函数的重新分配按以下二者之一执行:
a) 当前内存段后面剩余空间充足,则直接扩展这块内存,并且返回原指针。
b) 当前内存段后面剩余空间不足,则分配一个大小为 new_size 字节的新内存块,并复制大小等于新旧大小中较小者的内存区域,然后释放旧内存块。

若无足够内存,则不释放旧内存块,并返回空指针,原指针仍有效。


若 ptr 为 NULL ,则行为与调用 malloc(new_size) 相同。
若 new_size 为零,则相当于free(ptr)。

举例:

int* p1 = (int*)malloc(3 * sizeof(int));//分配最多可以容纳三个整型的空间

int* ptr = (int*)realloc(p1, 5 * sizeof(int));//重新分配五个整型的空间,原空间失效
if (ptr != NULL)
	p1 = ptr;
//使用完释放
free(ptr);
ptr = NULL;

动态内存函数常见错误

在使用内存函数开辟空间时,有许多常见错误,例如对NULL指针的解引用、多次free()同一块内存、free()非动态开辟的内存等。

错误1:(对NULL的解引用操作)

int* p1 = (int*)malloc(3 * sizeof(int));//分配最多可以容纳三个整型的空间
int* ptr = (int*)realloc(p1, 5 * sizeof(int));
p1 = ptr;
//使用完释放
free(ptr);
ptr = NULL;
return 0;

在realloc某一内存块时,如果重分配失败,返回空指针,这时原指针所指向的内容并没有被释放,而此时没有判断ptr是否为NULL,直接把ptr赋给p1,则导致原来的数据丢失,内存泄漏。

因此在使用内存函数后,必要的判断必不可少,即每次开辟空间后要判断指针是否为NULL。

错误2:(多次free()同一块内存)

int* p1 = (int*)malloc(3 * sizeof(int));//分配最多可以容纳三个整型的空间
int* ptr = (int*)realloc(p1, 0);
p1 = ptr;
//使用完释放
free(ptr);
ptr = NULL;//二次free同一块内存
return 0;

由于realloc(p1, 0)相当于free(p1),这时p1所指向的内容已经还给操作系统,不在归操作者使用,因此不能再对其free,这里free(ptr)相当于free了两次,可能造成程序崩溃。

而对于free(NULL),不会执行任何操作。

错误3:(free()非动态开辟的内存块)

int a[5] = { 1,2,3,4,5 };
int *p = a;
free(p);
p = NULL;

p所指向的内容属于静态内存,而free()是专门服务于动态内存的函数,因此强行free()非动态内存必然造成程序的崩溃。

此外动态内存忘记释放并且置空以及越界访问也是其常见错误之一。

例如:输出如下程序结果是什么?

void GetMemory(char** p, int num)
{
	*p = (char*)malloc(num);
}
void Test(void)
{
	char* str = NULL;
	GetMemory(&str, 100);
	strcpy(str, "hello");
	printf(str);
}

你一通思考后,发现好像并没有什么问题,在编译器上执行也没问题,于是坚定的得出输出结果为:hello。

可是它存在内存泄漏!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值