动态内存管理(C语言)

  在正式介绍动态内存管理前,我们先简单了解为什么会存在动态内存分配。

  我们常常使用的空间开辟方式是这样的:

int a = 20;//在栈空间上开辟四个字节
char arr[10] = { 0 };//在栈空间上开辟10个字节的连续空间

这样的开辟空间方式有两个特点:

      1.开辟空间的大小是固定的

      2.数组在声明的时候必须注意长度,所需内存在编译时分配

但是我们在某些情况下,程序运行所需的空间大小在程序运行的时候才能明确知道,比如我需要一个结构体数组来存放每个人的信息,随着我添加的信息增加,数组所需空间也会相应增加,这是,以上的空间开辟方式就不能满足需求了。于是,就有了动态内存开辟。

1.动态内存函数介绍

我们回顾一下内存分配方式, 局部变量、函数形参存放在栈上,而堆区是用来动态内存开辟的,

C语言提供的动态内存函数开辟函数有:malloc ,free ,realloc,calloc,头文件是<stdlib.h>。

 1.malloc 和 free

malloc函数向堆区申请一块连续可用空间,如果成功并且返回一个void*指针,如果失败返回空指针。

malloc函数的声明是这样的:

void* malloc ( size_t size );

其中size_t size是要开辟空间的字节数。我们以在堆区开辟一个存放10个整形的空间(其实就是一个存放10个元素的int数组,只不过位置不同)为例,看一下malloc使用方式:

#include <stdio.h>
#include <stdlib.h>
int main()
{
	//在堆区开辟10个int空间
	int* p = malloc(10 * sizeof(int));
    //以指针p来接收开辟的空间,转化为int*以便后续的使用
	if (p == NULL)//如果开辟失败返回NULL
		return;
	for(int i = 0;i < 10 ;i++)
    {
        *(p + i) = i;
        printf("%d ",p[i]);//可以数组使用方式来使用p
    }
    //释放空间并置为空指针
    free(p);
    p=NULL;
	return 0;
}

 当这块空间使用结束后,我们要使用free函数释放这块空间,还给操作系统,并且为了提高程序的安全性,我们在free后,我们最好要将p置为空指针。

 在使用malloc函数的时候要注意:

      1.如果开辟失败,会返回一个空指针,所以一定要检查。

      2.返回的类型是void*,所以malloc函数并不知道开辟空间的类型,要由使用者自己确定。

      3.如果参数size_t 是0,malloc的行为是标准未定义的,取决于编译器。

      4.使用完空间后要用free函数释放。

在使用free函数时要注意:

      1.如果free函数调用的参数指向的空间不是动态开辟的,那么free函数的行为是标准未定义的。

      2.如果free函数调用的参数是一个NULL指针,那么函数什么都不做。

2.calloc

calloc和malloc的功能类似,都是开辟一块连续的空间,calloc的声明如下:

void* calloc ( size_t num , size_t size );

calloc与malloc的不同是calloc有两个参数,一个是元素的个数,另一个是每个元素的字节大小。

calloc的具体功能是为num个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0而malloc并没有初始化。我们还是以开辟10个整型的动态内存为例看看calloc的使用:

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL)
		return;
	for (int i = 0; i < 10; i++)
		printf("%d ", p[i]);
	free(p);
	p = NULL;
	return 0;
}

跟malloc一样,在使用完calloc开辟的空间后,要用free释放,并且置为NULL指针。 

3.realloc

realloc用于重新开辟空间,对原开辟的动态内存进行调整。realloc声明如下:

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

其中,ptr指向之前开辟的内存空间(malloc,calloc开辟空间的首地址),size_t为调整之后新的大小(以字节为单位),返回值为调整之后内存的起始位置。

在之前的两个函数介绍中,我们以开辟10个int大小空间为例,现在我们要增加之前开辟的空间,来存放20个整型。

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int* p = calloc(10,sizeof(int));
	if (p == NULL)
		return 0;
	for (int i = 0; i < 10; i++)
		*(p + i)= i;
	//对之前的空间进行调整,将空间大小增加10个int
	int* ptr = realloc(p, 20 * sizeof(int));
	//为什么要用ptr接收,而不以p直接接收?
	if (ptr != NULL)
		p = ptr;
	free(p);
	p = NULL;
	return 0;
}

那么为什么要先用ptr接收呢?其实,realloc在调整空间时有两种情况:

情况1:原有空间之后有足够大的空间

        在这种情况下,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化,ptr和p指向相同的空间。

情况 2 :原有空间之后没有足够大的空间

 

在这种情况下,原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小的连续空间来使用,这样函数返回的是一个新的内存地址,ptr和p指向不同的空间。

2.动态内存使用误区

1. 对NULL指针进行解引用操作

就像之前所说的,我们在开辟完一块内存后,一般要进行检查,以防开辟失败返回了空指针。如果我们没有意识到内存开辟失败,直接对p所指向的空间进行操作,那么程序就会产生一系列问题。

void test()
{
	int* p = (int*)malloc(INT_MAX / 4);
	*p = 20;//如果p的值是NULL,就会有问题
	free(p);
}

2.对非动态开辟内存使用free释放

 之前提过,free函数只能释放动态开辟的空间,如果我们传入一个指向非动态开辟空间的指针,就会导致程序报错。

void test()
{
	int a = 1;
	int* p = &a;
	free(p);
}

 

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

在使用free函数释放开辟的空间时,一定要将所有空间释放,即我们传入的指针必须指向开辟空间的其实位置,否则程序会报错。 

void test()
{
	int* p = (int*)malloc(100);
	p++;
	//p不再指向动态内存的起始位置
	free(p);
}

  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ChenxuanRao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值