最全动态内存分配:malloc、calloc、realloc(超详解析,多维度分析,2024年最新最新阿里P7技术体系

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

🍊free()函数

知识点1:
🔑 free()函数的头文件:#include <stdlib.h>

🔑 free()函数的声明:

函数声明的解释:

ptr : 指向先前用malloc、calloc或realloc分配的内存块的指针

🔑 free()函数的功能:

释放申请的动态内存分配的空间(即malloc、calloc、realloc函数申请的空间)具体情况:

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

▶ 如果参数 ptr 是 NULL 指针,那么 free 将不会执行任何动作。

🔑注意事项:
▶ 使用完之后一定要记得使用 free 函数释放所开辟的内存空间。

▶ 使用指针指向动态开辟的内存,使用完并 free 之后一定要记得将其置为空指针,防止越界访问。

原因:free()函数只会释放ptr指向空间的值,但ptr本身不会被置空。

🔑代码举例:

#include <stdio.h>
#include <stdlib.h>
int main()
{
	//申请10个int类型大小空间,10 * sizeof(int)相对于sizeof(40)更具有移植性
	//由于malloc返回值为void*类型,所以强制类型转换为int*类型
	int n; // 开辟空间数  举例为 10
	printf("请输入想开辟的 int 数组大小\n");
	scanf("%d", &n);
	int* p = (int*)malloc(n * sizeof(int));
	if (p == NULL) 
	{
		exit(EXIT_FAILURE);   //p 现在指向有 n 个元素的数组
	}
	int i = 0;
	for (i = 0; i < 10; i++) //打印这10个元素
	{
		printf("%d ", *(p + i));
	}
	printf("\n");
	for (i = 0; i < 10; i++) //对数组元素赋值
	{
		p[i] = i;    // 此时可以将 指针 p 看作数组名
	}
	for (i = 0; i < 10; i++) //打印这10个元素
	{
		printf("%d ", *(p + i));
	}
	printf("\n");
	free(p); //释放p所指向动态内存分配的空间
	p = NULL;//将p置为NULL指针,防止访问一个已释放的空间
	return 0;
}

运行结果:

请输入想开辟的 int 数组大小:
10
-842150451 -842150451 -842150451 -842150451 -842150451 -842150451 -842150451 -842150451 -842150451 -842150451
0 1 2 3 4 5 6 7 8 9

代码解释:

▶ 开始 指针 p 指向 int 类型 开辟的40个字节空间,并没有赋值,所以输出的值都是随机的。

 exit()函数。该函数的原型在 stdlib.h 中,用来在内存分配失败时结束程序

值 EXIT_FAILURE 也在这个头文件中定义。标准库提供了两个保证能够在所有操作系统       下工作的返回值:
   EXIT SUCCESS(或者,等同于0)指示程序正常终止;
   EXIT_FAILURE指示程序异常终止。

知识点2:内存泄露
大家是否有跟我一样的想法,目前我们知道了动态内存的开辟函数 malloc() ,之后便出现了释放函数 free(),我有点好奇,在我开辟函数之后,我不去释放,你能把我怎样呢?

抱着这样的想法,我做了一个实验:

#include<stdio.h>
#include<malloc.h>

void gobble (double ar[], int n);

int main()
{ 
	double glad[2000];
	int i;
	
   for(i = 0; i<100000000; i++)
  {
		gobble(glad, 2000);
  }
}

void gobble(double ar[], int n)
{
	double *temp = (double *) malloc(n*sizeof(double)) ;
	
//	free(temp);/*忘记使用*/    //我就皮,我就用
}

代码解释:
▶ 第一次调用gobble()时,它创建了指针temp,并使用malloc()为之分配16000字节        的内存(设double是8个字节)。
    假定我们如暗示的那样没有使用free()。
    当函数终止时,指针temp作为一个自动变量消失了。
    但它所指向的16000个字节的内存仍旧存在。
    我们无法访问这些内存,因为地址不见了。
    由于没有调用free(),不可以再使用它了。

▶****第二次调用gobble(),它又创建了一个temp,再次使用malloc()分配16000个字节        的内存。
   第一个16000字节的块已不可用,因此malloc()不得不再找一个l6000字节的块。
  当函数终止时,这个内存块也无法访问,不可再利用。

▶ 但循环执行了1000次,因此在循环最终结束时,已经有1600万字节的内存从内存池中移        走。事实上,在到达这一步前,程序很可能已经内存溢出了。

**▶这类问题被称为内存泄漏(memory leak)****,**可以通过在函数末尾处调用free()防止该问题出现。

🍌calloc()函数

知识点1:
🔑****calloc()函数的头文件:#include <stdlib.h>

🔑****calloc()函数的声明:

🔑****解释函数声明:
▶****num:元素个数

▶****size: 元素大小

▶****返回值:申请成功返回该空间起始地址,申请失败返回NULL指针,因为不知道申请的空间要存放什么类型数据所以返回void*类型。

🔑calloc()函数的功能:
calloc函数与malloc函数功能一样,区别主要在于calloc会对分配的空间初始化为0,另外它们请求内存大小的方式不同。

🔑验证malloc()函数与calloc()函数的区别:
   malloc()函数:

#include <stdio.h>
#include <stdlib.h>
 
int main()
{
    // malloc
    int* p = (int*)malloc(40); // 开辟40个空间
    if (p == NULL) 
	{
		exit(EXIT_FAILURE);   //p 现在指向有 10 个元素的数组
	}
    int i = 0;
    for (i = 0; i < 10; i++)
        printf("%d ", *(p + i));
    free(p);
    p = NULL;
 
    return 0;
}

运行结果:

// 随机的 10 个值 

calloc()函数:

#include <stdio.h>
#include <stdlib.h>
 
int main()
{
    // calloc
    int* p = (int*)calloc(10, sizeof(int)); // 开辟10个大小为int的空间,40
   if (p == NULL) 
	{
		exit(EXIT_FAILURE);   //p 现在指向有 10 个元素的数组
	}
    int i = 0;
    for (i = 0; i < 10; i++)
        printf("%d ", *(p + i));
    free(p);
    p = NULL;
 
    return 0;
}

运行结果:

0 0 0 0 0 0 0 0 0 0

🔑****总结:

说明 calloc 会对内存进行初始化,把空间的每个字节初始化为 0 。如果我们对于申请的内存空间的内容,要求其初始化,我们就可以使用 calloc 函数来轻松实现。

🍇realloc()函数

知识点1:
🔑****realloc()函数的头文件:#include <stdio.h>

🔑****realloc()函数的声明:

🔑声明的解释:

▶ ptr:指向先前用malloc、calloc或realloc分配的内存块的指针

▶ size:动态内存空间新大小,单位为字节

▶ 返回值:返回调整后空间的起始地址,调整失败返回NULL指针

🔑realloc()函数的功能:
让动态内存管理更加灵活。用于重新调整之前调用 malloc 或 calloc 所分配的ptr 所指向的内存块的大小,可以对动态开辟的内存进行大小的调整。

🔑realloc()函数调整内存的 4 中情况:(看图解)
▶ 情况一:在原有的基础上扩大空间

原有空间之后有足够大的空间。

▶ 情况二:原有空间之后没有足够大的空间。

原空间之后没有足够多的空间满足扩展需求,在内存上另外寻找一个适合大小的来连续空间来使用,并将原空间数据先复制过来然后释放空间。这样函数返回的是一个新空间的内存地  址

▶ 情况三:缩小原有的空间

原空间尾部的部分空间被释放,剩余空间数据依旧保留。

▶ 情况四:

🔑代码演示:
realloc()调整函数大小:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int* p = (int*)calloc(10, sizeof(int));
    if (p == NULL)
    {
        exit(EXIT_FAILURE);   //p 现在指向有 10 个元素的数组
    }
    // 使用
    int i = 0;
    for (i = 0; i < 10; i++) {
        *(p + i) = 5;
    }
    // 此时,这里需要p指向的空间更大,需要20个int的空间
    // realloc 调整空间
    p = (int*)realloc(p, 20 * sizeof(int)); // 调整为20个int的大小的空间
    // 释放
    free(p);
    p = NULL;
}

疑问: 刚才提到的第四种情况,如果 realloc 找不到合适的空间,就会返回空指针。我们想让它增容,他却存在返回空指针的危险,这怎么行?

🔑代码优化:

#include <stdio.h>
#include <stdlib.h>
 
int main() 
{
    int* p = (int*)calloc(10, sizeof(int));
    if (p == NULL)
    {
        exit(EXIT_FAILURE);   //p 现在指向有 n 个元素的数组
    }
    // 使用
    int i = 0;
    for (i = 0; i < 10; i++) 
    {
        *(p + i)  = 5;
    }
    // 此时,这里需要 p 指向的空间更大,需要 20 个int的空间
    // realloc 调整空间
    int* ptmp = (int*)realloc(p, 20*sizeof(int));
    // 如果ptmp不等于空指针,再把p交付给它
    if (ptmp != NULL) 
    {
        p = ptmp;
    }
 
    // 释放
    free(p);
    p = NULL;
    return 0;
}

🔑代码演示全过程;

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main()
{
	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL)
	{
		exit(EXIT_FAILURE);   //p 现在指向有 10 个元素的数组
	}
	int i = 0;
	for (i = 0; i < 10; i++) //对数组元素赋值
	{
		*(p + i) = i;
	}
	int* ptr = (int *)realloc(p, 20 * sizeof(int)); //对动态内存大小进行调整
	if (ptr == NULL) //调整失败并不影响原本p指向空间
	{
		printf("空间调整失败\n");
	}
	else
	{
		p = ptr; //调整成功,p指向调整后空间起始地址
		ptr = NULL;
		for (i = 10; i < 20; i++) //对数组元素赋值
		{
			*(p + i) = i;
		}
		for (i = 0; i < 20; i++) //打印数组元素
		{
			printf("%d ", *(p + i));
		}
	}
	free(p); //释放p所指向动态内存分配的空间
	p = NULL;//将p置为NULL指针,防止访问一个已释放的空间
	return 0;
}

运行结果:

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

知识点2:

有趣的是,其实你可以把 realloc 当 malloc 用:

// 在要调整的内存地址部分,传入NULL:
int* p = (int*)realloc(NULL, 40); // 这里功能类似于malloc,就是直接在堆区开辟40个字节

四、常见动态内存分配的错误

**知识点1:**使用 free 释放一块动态开辟内存的一部分

错误代码演示:

#include <stdio.h>
#include <stdlib.h>
 
int main()
{
    int* p = (int *)malloc(10*sizeof(int));
     if (p == NULL)
    {
        exit(EXIT_FAILURE);   //p 现在指向有 10 个元素的数组
    }

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

nclude <stdio.h>

#include <stdlib.h>

int main()
{
int* p = (int )malloc(10sizeof(int));
if (p == NULL)
{
exit(EXIT_FAILURE); //p 现在指向有 10 个元素的数组
}

[外链图片转存中…(img-B5TC4I2O-1715825633760)]
[外链图片转存中…(img-P6jDMQcH-1715825633761)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值