动态内存管理

1.为什么要有动态内存分配

我们已经掌握的内存开辟方式有:

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

但是上述的开辟空间的方式有两个特点:

空间开辟的大小是固定的。

数组在申明的时候,必须指定数组的长度,数组空间一旦确定了大小不能调整。

但是对于空间的要求,不仅仅是上述的情况,有时候我们需要的空间大小在程序运行的时候才能知道,那数组的编译时开辟空间的方式就不能满足。

C语言引入了动态内存开辟,让程序员自己可以申请和释放空间,就比较灵活了。

2.malloc和free

2.1malloc

  malloc   函数是 C 语言标准库中的一个函数,用于动态分配内存。它在   <stdlib.h>   头文件中声明,用于在堆(heap)上分配指定大小的内存块,并  返回  指向该内存块的指针如果分配失败,它会返回   NULL   指针。
 
  malloc   函数的基本语法如下:

void* malloc (size_t size);

 1.其中   size_t   是一个无符号整数类型,用于指定要分配的字节数。
 2.使用   malloc   函数时,你需要包含   <stdlib.h>   头文件,并且在使用完毕后,应该使用   free   函数来释放分配的内存,以避免内存泄漏。(关于什么free我们下面会说)

3.返回值的类型是void*,所以malloc函数并不知道开辟空间的类型具体在使用的时候,使用者自己来决定

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

2.2free

  free   函数是 C 语言标准库中的一个函数,用于释放之前通过   malloc  、  calloc   或   realloc   函数动态分配的内存。它在   <stdlib.h>   头文件中声明。
 
当你使用   malloc   或其他动态内存分配函数分配内存后,这些内存会一直被占用,直到你明确地使用   free   函数来释放它。如果不释放不再使用的内存,会导致内存泄漏,即内存资源被占用但无法被其他程序或系统回收,这可能会导致系统性能下降。

C语言提供了另外一个函数free,专门是用来做动态内存的释放和回收的,函数原型如下:

void free (void* ptr);

free函数用来释放动态开辟的内存。

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

2.如果参数ptr是NULL指针,则函数什么事都不做。

malloc和free都声明在stdlib.h头文件中 

下面我给出一串代码,来解释一下malloc和free函数要怎么使用。

#include <stdio.h>
#include <stdlib.h>

int main()
 {
    int *ptr;
    int n = 10; // 假设我们想要分配10个整数的空间

    // 分配内存
    ptr = (int*)malloc(n * sizeof(int));
    if (ptr == NULL) {
        // 如果分配失败,打印错误信息并退出程序
        fprintf(stderr, "Memory allocation failed\n");
        return 1;
    }

    // 使用分配的内存
    for (int i = 0; i < n; i++) 
    {
        ptr[i] = i;
    }

    // 打印分配的内存中的值
    for (int i = 0; i < n; i++) 
    {
        printf("%d ", ptr[i]);
    }
    printf("\n");

    // 释放内存
    free(ptr);

    // 注意:释放内存后,ptr 指针仍然指向原来的内存地址,但该内存区域已经不再属于程序。
    // 因此,不应该再使用 ptr 指针访问内存,除非重新分配。

    return 0;
}

 3.calloc和realloc

3.1calloc

  calloc   函数是 C 语言标准库中的一个函数,用于动态内存分配。它与   malloc   函数类似,但有两个主要区别:
 
1. calloc   会将分配的内存初始化为零,而   malloc   不会初始化内存。
2.  calloc   接受两个参数:元素的数量 和 每个元素的大小,而   malloc   只接受一个参数,即所需内存的大小。

C语言还提供一个函数叫calloc,calloc函数也用来动态内存分配。原型如下:

void* calloc (size_t num, size_t size);

  num   是要分配的元素数量。
  size   是每个元素的大小(以字节为单位)。
  calloc   返回一个指向分配内存的指针,如果分配失败,则返回   NULL  。

函数的功能是为num个大小为size元素开辟一块空间,并把空间的每一个字节初始化为0

与函数malloc的区别只在于calloc会在返回地址之前把申请的空间的每一个字节初始化为0

我们可以给一串代码,来讲解一下:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
int main()
{
	int* p = (int*)calloc(10, sizeof(int));//开辟了十个空间,每一个空间的大小就是sizeof(int),并且把每一个字节都初始化成了0。
	if (p != NULL)
	{
		int i = 0;
		for (i = 0; i < 10; i++)
		{
			printf("%d ", *(p + i));
		}
	}
	free(p);
	
		p = NULL;
	return 0;
}

结果就是这样的:

所以如果我们对申请的内存空间的内容要求初始化,那么可以很方便的使用calloc函数来进行任务。

3.2realloc 

realloc函数的出现让动态内存管理更加灵活。

  realloc   函数是 C 语言标准库中的一个函数,用于重新分配动态分配的内存。当你已经使用   malloc  ,   calloc  , 或   realloc   分配了一块内存,但后来发现需要更多的空间或更少的空间时,可以使用   realloc   来调整内存块的大小

realloc函数的原型如此下:

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

  ptr   是指向之前分配的内存块的指针。
  size   是新的内存块大小(以字节为单位)。

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

  realloc   返回一个指向重新分配的内存块的指针,如果分配失败则返回   NULL  。如果新的内存块大小 小于原来的大小,那么原始内存块中超出新大小的部分将被丢弃。如果新的内存块大小大于原来的大小,那么原始内存块的内容将保持不变,新分配的内存将不会被初始化。

realloc再调整内存空间的是存在两种情况:

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

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

下面的代码把空间调整为40个字节就包含两种情况,下面会细细道来:

 情况1:

当情况1的时候,要扩展内存就直接在原有内存之后直接追加空间,原来空间的数据不发生变化。

情况2:

当时情况2的时候,红色区域假设已经分配了,原有空间之后没有足够多的空间时,扩展的方法是:

1.在堆空间上另找一个适合大小的连续空间来使用

2.会在原来的空间的数据拷贝一份到新的空间

3.释放旧的空间。(不用对旧的空间free因为这个函数就会直接把旧的空间释放掉)

 4.返回新的内存空间的起始地址

由于有这两种情况,realloc函数使用的时候就要注意一些:这里给出一串代码;

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
int main()
{
	int* ptr = (int*)malloc(100);
	if (ptr != NULL)
	{
		//业务处理
	}
	else
	{
		return 1;
	}
	//扩展容量

	//代码1 - 直接将realloc的返回值放到ptr中
	ptr = (int*)realloc(ptr, 1000);//这样可以吗?答案是不可以,因为你不确定这个空间是否会请求成功。
	//如果请求失败的话就会变成空指针,就会找不到这个地址了
	//所以说换一种思路:
	//代码2 - 先将realloc函数的返回值放到p中,不为NULL,放到 ptr中
	int* p = NULL;//对p进行初始化
	p = realloc(ptr, 1000);
	if (p != NULL)	
	{
	   ptr = p;
	//这时候就可以使用它了
	   int i = 0;
	   for (i = 5; i < 10; i++)
	   {
		   *(p + i) = i + 1;
	   }
	   for (i = 0; i < 10; i++)
	   {
		   printf("%d", *(p + i));
	   }
	   free(p);
	   p = NULL;
	}
	else
	{
		perror("realloc");
	}
	//到这一步就是请求成功的话,就会是使用40个字节的空间了
		//业务处理
	free(ptr);
	return 0;
}

OK了兄弟们关于动态内存申请涉及到的函数到这讲完了,但是这个章节还是有很多知识,我们下篇在开始。 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值