动态内存分配:malloc、calloc、realloc(超详解析,多维度分析,小白一看就懂!!!!)

这篇具有很好参考价值的文章主要介绍了动态内存分配:malloc、calloc、realloc(超详解析,多维度分析,小白一看就懂!!!!)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一、前言

二、动态内存的简单介绍

🍉什么是动态内存分配

🍎为什么要使用动态内存分配 

三、动态内存函数的介绍和拓展 

🍋malloc()函数 

🍊free()函数 

🍌calloc()函数

🍇realloc()函数

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

五、共勉


一、前言

在学习动态内存分配时,感觉这些动态分配没什么用,也就没在意跳过去了,直到碰到数据结构和一些需要动态数组的题目时才知道,动态内存分配的重要性。这次专门花了一早上的时间来学习了动态内存分配,并将它分享出来,希望对大家有帮助哦!!!!

二、动态内存的简单介绍

🍉什么是动态内存分配

知识点1:

🔑目前在我们平时写代码的过程中接触最多的就是在栈空间上开辟连续的空间:

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

🔑此时我们需要注意在栈空间上的开辟空间的特点:

▶空间开辟的大小是固定的。
▶数组在声明时必须指定数组的长度,在编译时会开辟并分配其所需要的内存空间。

知识点2:

所谓动态内存分配(Dynamic Memory Allocation) 就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不象数组等静态内存分配方法那样需要预先分配存储空间而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小。

malloc和realloc,C语言超详细解析,jvm,c++,数据结构,c语言

🍎为什么要使用动态内存分配 

知识点1:
 🔑之前学习了数组,数组的元素储存在内存中连续位置。在声明数组时必须要指定数组的元素个数,即数组空间大小在声明时已经确定了但是需存放的元素个数常常在运行时才能知道(取决于输入的数据)。

🔑此时应用栈去申请内存空间的缺点就出现了:

1. 当输入元素个数大于数组声明的元素个数时会带来意想不到错误

2. 当输入元素个数小于数组声明的元素个数时会带来内存空间的浪费

3.数组大小不能动态调整

💡 总结:有时我们需要的空间大小在程序运行的时候才能知道,这时在数组编译时开辟空间的方式就不能满足了,这时我们就需要动态内存开辟来解决问题。

三、动态内存函数的介绍和拓展 

🍋malloc()函数 

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

🔑malloc()函数的声明:

malloc和realloc,C语言超详细解析,jvm,c++,数据结构,c语言

 函数声明的解释:
size_t 表示无符号类型       size:表示申请分配的内存大小,单位为字节

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

🔑malloc()函数的功能介绍

malloc 是C语言提供的一个动态内存开辟的函数,该函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。具体情况如下:

▶ 如果开辟成功,则返回一个指向开辟好空间的指针。

▶ 如果开辟失败,则返回一个 NULL 指针。

▶ 如果开辟失败,则返回一个 NULL 指针。

▶ 如果 size 为 0(开辟0个字节),malloc 的行为是标准未定义的,结果将取决于编译器。

🔑注意:

1. malloc返回值有可能是NULL指针,使用前需要检查
2. malloc申请的空间并没有被初始化


🔑代码举例说明:

     用malloc()来创建一个 数组。可以在程序运行时使用malloc()请求一个存储块,另外还需要一个指针来存放该块在内存中的位置。

int * ptd;
ptd = (int * ) malloc (30 * sizeof(int));
if (ptd == NULL) //空间申请失败则退出
{
	return -1; 
}

代码解释:

▶ 这段代码请求30个 int类型 值的空间,并且把ptd指向该空间所在位置。
▶ 注意: ptd是作为指向一个 int类型值 的指针声明的,而不是指向30个 int类型 值的数据块的指针。

🍊free()函数 

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

🔑 free()函数的声明:

malloc和realloc,C语言超详细解析,jvm,c++,数据结构,c语言

  函数声明的解释:

 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

代码解释:

malloc和realloc,C语言超详细解析,jvm,c++,数据结构,c语言

▶ 开始 指针 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()函数的声明:
malloc和realloc,C语言超详细解析,jvm,c++,数据结构,c语言

 🔑解释函数声明:
▶ 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()函数的声明:
malloc和realloc,C语言超详细解析,jvm,c++,数据结构,c语言

 🔑声明的解释:

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

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

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

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

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

malloc和realloc,C语言超详细解析,jvm,c++,数据结构,c语言

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

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

malloc和realloc,C语言超详细解析,jvm,c++,数据结构,c语言

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

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

malloc和realloc,C语言超详细解析,jvm,c++,数据结构,c语言

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

▶ 情况四:
malloc和realloc,C语言超详细解析,jvm,c++,数据结构,c语言 

🔑代码演示:
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 个元素的数组
    }
    int i = 0;
    for (i = 0; i < 5; i++) {
        *p++ = i; // p指向的空间被改变了
    }
 
    free(p);
    p = NULL;
  
    return 0;
}

错误原因分析:
malloc和realloc,C语言超详细解析,jvm,c++,数据结构,c语言

 此时free(p)出现了大问题,释放的是后面的空间。不能从一块动态开辟的内存空间的某一部分释放,必须从头开始释放。

代码更新:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int* p = (int *)malloc(10 * sizeof(int));
    if (p == NULL)
    {
        exit(EXIT_FAILURE);   //p 现在指向有 10 个元素的数组
    }
    int* ptd = p;    //提前将 p 指针与 ptd 指针进行替换
    int i = 0;
    for (i = 0; i < 5; i++) {
        *ptd++ = i; // ptd指向的空间被改变了
    }
    free(p);
    p = NULL;
    return 0;
}

知识点2:
 问题:💬 下列代码存在什么问题?请指出问题并做出相应的修改。
 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
void GetMemory(char *p) {
    p = (char*)malloc(100);
}
 
void Test() {
    char *str = NULL;
    GetMemory(str);
    strcpy(str, "hello world");
    printf(str);
}
 
int main() {
    Test();
    
    return 0;
}

💡 参考答案:str 传给 GetMemory 函数时为值传递,所以 GetMemory 函数的形参 p 是 str 的一份临时拷贝。在 GetMemory 函数内部动态开辟的内存空间的地址存放在了 p 中,并不会影响 str。所以当 GetMemory 函数返回之后, str 仍然是 NULL,导致 strcpy 拷贝失败。其次,随着 GetMemory 函数的返回,形参 p 随即销毁并且没有及时的使用 free 释放,从而导致动态开辟的100个字节存在内存泄露问题。根据经验,程序会出现卡死的问题。

修改代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
// ↓ 修改返回类型为char*
char* GetMemory(char *p) {
    p = (char*)malloc(100);
    return p; // 将p带回来
}
 
void Test() {
    char *str = NULL;
    str = GetMemory(str); // 用str接收,此时str指向刚才开辟的空间
    strcpy(str, "hello world"); // 此时copy就没有问题了
    printf(str);
    // 用完之后记得free,就可以解决内存泄露问题
    free(str);
    str = NULL; // 还要将str置为空指针
}
 
int main() {
    Test();
 
    return 0;
}

运行结果:

  hello world

五、共勉

 以下就是我对动态内存分配的理解,如果有不懂和发现问题的小伙伴,请在评论区说出来哦,同时我还会继续更新对数据结构-------顺序表的理解,请持续关注我哦!!!!! 

malloc和realloc,C语言超详细解析,jvm,c++,数据结构,c语言

 文章来源地址https://www.toymoban.com/news/detail-743387.html

到了这里,关于动态内存分配:malloc、calloc、realloc(超详解析,多维度分析,小白一看就懂!!!!)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C语言——动态内存管理(malloc, calloc, realloc, free, 柔性数组详解)

    我们以往定义数组,都是这么定义的: 以这种方式开辟空间有两个特点: 空间开辟的大小是固定的 数组在声明的时候,必须指定数组的长度,它所需要的内存在编译时分配 因此就导致了这样一个现象:我们无法在后续的过程中修改数组的大小,这是一个十分麻烦的事情 而

    2024年02月16日
    浏览(53)
  • 动态内存malloc,calloc,realloc如何使用,使用场景及使用free释放内存时崩溃的原因

    目录 1.内存区域 2.void与void* 3.应用场景 4.malloc 5.calloc 6.realloc 7.free崩溃的原因 7.1引入 7.2具体原因 7.2.1越界 7.2.2指针移动 7.2.3重复释放同一段内存 局部变量 : 定义在函数内部的变量 , 包括形参 , 在栈 (stack) 中 , 作用域在函数内部有效 , 生存周期 : 进入函数创建, 退出函数销毁。

    2024年02月05日
    浏览(57)
  • C语言中灵活多变的动态内存,malloc函数 && free函数&& calloc函数 && realloc函数

    铁子们好啊!今天阿辉给大家讲一下C语言里面特别重要的一块知识——动态内存管理,为什么说它重要呢?相信大家在使用数组时都遇到过数字开辟空间太大或者太小的情况,这是很尴尬的因为数组空间一旦确定就难以扩大或者缩小。而今天的动态内存管理将帮我们很好的解

    2024年02月03日
    浏览(39)
  • calloc、malloc、realloc函数的区别及用法

    三者都是分配内存,都是stdlib.h库里的函数,但是也存在一些差异。 (1)malloc函数。其原型void *malloc(unsigned int num_bytes); num_byte为要申请的空间大小,需要我们手动的去计算,如int *p = (int )malloc(20 sizeof(int)),如果编译器默认int为4字节存储的话,那么计算结果是80Byte,一次申请

    2024年02月08日
    浏览(45)
  • C语言 malloc动态内存分配函数

    malloc函数:malloc时动态内存分配函数,用于申请一块连续的指定大小的内存块区域以void*类型返回分配的内存区域地址,就是当数组创建长度不一定 害怕数据存储不够或者不能浪费时间 在使用malloc开辟空间时,使用完成一定要释放空间,如果不释放会造内存泄漏。n在使用ma

    2024年02月07日
    浏览(45)
  • 16 malloc 虚拟内存分配的调试(1)

    呵呵 在 c 语言中 malloc 应该是初学者必须了解的一个函数了吧  但凡 涉及到堆内存分配的相关, 必定会使用到 malloc, realloc, calloc 这几个函数  其中 malloc 最常见, 也是最 实用  在 HotspotVM 中也经常会看到 malloc 的身影  我们这里 来调试一下 malloc 的相关的一些场景  本文主要的

    2023年04月09日
    浏览(43)
  • 从 malloc 分配大块内存失败 来简看 linux 内存管理

    应用进程 malloc 返回了null,但是观察到的os 的free内存还有较大的余量 ,很奇怪为什么会这样? 不可能是oom导致的(当然也没有 os 的oom 日志),free还有余量,系统也没有cgroup的应用隔离。 我们linux上使用的库函数 malloc 基本都是用glibc库实现的malloc函数(当然如果binary 链接

    2024年02月08日
    浏览(53)
  • C++与C语言动态内存管理的不同 new与malloc

      目录 1.C语言动态内存管理方式 2.C++中动态内存管理 2.1 new和delete操作内置类型 2.2 new和delete操作自定类型 2.3 为什么delete要带[ ]  3.new申请空间失败 4.operator new 与 operator delete 函数 5.new与delete的是实现原理 5.1 内置类型 5.2 自定义类型 6.定位new表达式(了解即可) 7.malloc/free和

    2024年02月08日
    浏览(41)
  • 【C/C++】静态内存分配与动态内存分配

    1.1 - 定义概述 内存分配 (Memory Allocation) 是指为计算机程序或服务分配物理内存空间或虚拟内存空间的一个过程。通常在程序执行前或执行时完成内存分配。 1.2 - 分类概述 存在两种类型的内存分配: 编译时内存分配或静态内存分配 (Compile-time or Static Memory Allocation) 运行时内存

    2024年02月11日
    浏览(41)
  • C++——内存分配与动态内存管理

    🌸作者简介: 花想云 ,在读本科生一枚,致力于 C/C++、Linux 学习。 🌸 本文收录于 C++系列 ,本专栏主要内容为 C++ 初阶、C++ 进阶、STL 详解等,专为大学生打造全套 C++ 学习教程,持续更新! 🌸 相关专栏推荐: C语言初阶系列 、 C语言进阶系列 、 数据结构与算法 本章我们

    2023年04月17日
    浏览(56)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包