当前位置: 首页 > news >正文

动态内存分配

一、为什么要有动态内存分配

在这里插入图片描述

二、malloc和free

在这里插入图片描述
栈区中的数据出了作用域就会销毁;而静态区中数据的生命周期与全局变量一致,出了作用域也不会被销毁,直至程序结束后才会销毁。

malloc函数与free函数需要包含的头文件是<stdlib.h>

①malloc

在这里插入图片描述

②free

在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
int main()
{//开辟10个整型的空间int* p = (int*)malloc(10 * sizeof(int));//p的值是开辟的10个整型空间中,第一个整型空间的地址assert(p != NULL);for (int i = 0; i < 10; i++){*(p + i) = 1 + i;}return 0;//释放空间//free将申请的10个整型的空间释放后,p中依然存放的是原来申请的10个整型中,第一个整型空间的地址,此时p就是野指针free(p);p=NULL;//如果不用free函数手动释放这块空间的话,等程序运行结束后,这块空间也会自动被操作系统回收。}

二、calloc与realloc

使用calloc与realloc函数需要包含的头文件是<stdlib.h>

①calloc

在这里插入图片描述

#include<stdlib.h>
#include<assert.h>
#include<stdio.h>
int main()
{int* p = (int*)calloc(10, sizeof(int));//向内存申请10个整型空间,并将每个字节初始化为0//p中存放的是第一个整型空间的地址assert(p != NULL);for (int i = 0; i < 10; i++){printf("%d ", p[i]);//p[i]等价于*(p+i)}//释放空间free(p);//free函数将这10个整型空间回收后,p中存放的依旧是原先第一个整型空间的地址,此时p就是野指针p = NULL;return 0;
}

②malloc与calloc的区别

a.malloc函数只有一个参数,而calloc函数有两个参数
b.calloc函数会将动态开辟的每个字节初始化为0,而malloc函数动态开辟的每个字节都是随机值。

③realloc

在这里插入图片描述

a.realloc函数的介绍

有时会我们发现原先动态开辟的空间太小了(或者太大了),那为了合理的使用内存,我们需要对该内存的大小做灵活的调整。那么 realloc 函数就可以调整动态开辟的内存大小。

b.realloc函数调整动态开辟的空间时的两种情况:

情况1:原有空间之后有足够大的空间时,若想扩展空间的话,就直接在原有空间之后直接扩展,且原来空间的数据不发生变化。

#include<stdlib.h>
#include<assert.h>
#include<stdio.h>
int main()
{int* p = (int*)calloc(10, sizeof(int));//向内存申请10个整型空间,并将每个字节初始化为0//p中存放的是第一个整型空间的地址assert(p != NULL);for (int i = 0; i < 10; i++){printf("%d ", p[i]);//p[i]等价与*(p+i)}//将原先动态开辟的10个整型空间扩展为12个整型空间int* ptr = (int*)realloc(p, 12 * sizeof(int));//用一个新变量ptr来接收新的空间的起始地址的目的:如果用p来接收的话,倘若扩展空间失败了,则p的值变为NULL了,此时原来扩展的10个整型空间就找不到了。assert(ptr != NULL);p = ptr;//释放空间free(p);p = NULL;ptr = NULL;return 0;
}

在这里插入图片描述
情况2:原有空间之后没有足够大空间,扩展的方法是:在堆区上另找⼀个大小合适
的连续空间来使用,并将旧的数据拷贝到新的空间,然后回收旧的空间。此时realloc函数返回的是⼀个新的内存地址。

#include<stdlib.h>
#include<assert.h>
#include<stdio.h>
int main()
{int* p = (int*)calloc(10, sizeof(int));//向内存申请10个整型空间,并将每个字节初始化为0//p中存放的是第一个整型空间的地址assert(p != NULL);for (int i = 0; i < 10; i++){printf("%d ", p[i]);//p[i]等价与*(p+i)}//将原先动态开辟的10个整型空间扩展为50个整型空间int* ptr = (int*)realloc(p, 50 * sizeof(int));//用一个新变量ptr来接收新的空间的起始地址的目的:如果用p来接收的话,倘若扩展空间失败了,则p的值为NULL,此时原来扩展的10个整型空间就找不到了。assert(ptr != NULL);p = ptr;//释放空间free(p);p = NULL;ptr = NULL;return 0;
}

在这里插入图片描述

④realloc除了可以调整动态开辟空间的大小外,还可以动态开辟空间

#include<stdlib.h>
#include<assert.h>
int main()
{int* p = (int*)realloc(NULL, 10 * sizeof(int));//等价于int* p = (int*)malloc(10 * sizeof(int));//在堆区申请了10个连续整型的空间assert(p != NULL);//使用空间//......//回收空间free(p);p = NULL;return 0;
}

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

①对空指针解引用

#include<stdlib.h>
int main()
{int* p = (int*)malloc(10 * sizeof(int));*p = 20;//p如果是空指针,编译器就会报错return 0;

②对动态开辟的空间造成了越界访问

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
int main()
{//开辟10个整型的空间int* p = (int*)malloc(10 * sizeof(int));assert(p != NULL);for (int i = 0; i < 40; i++){*(p + i) = 1 + i;}//只动态开辟了10个整型空间,却访问了40个整型的空间,造成了越界访问。return 0;//释放空间free(p);p=NULL;}

③对非动态开辟的空间使用free来回收

#include<stdlib.h>
int main()
{int arr[10] = { 0 };free(arr);return 0;
}

④使用free函数回收动态开辟空间的一部分

#include<stdlib.h>
#include<assert.h>
int main()
{int* p = (int*)malloc(10 * sizeof(int));assert(p != NULL);for (int i = 0; i < 5; i++){*p = i;p++;}free(p);//此时p指向的不是动态开辟空间的起始位置,编译器将会报错p = NULL;return 0;
}

⑤动态开辟的空间忘记回收(造成内存泄漏)

void test()
{int* p = (int*)malloc(100);if (p != NULL){*p = 20;}
}
int main()
{test();//......return 0;
}
//直至程序执行到return 0 前,动态开辟的10个整型空间不会被回收(当动态开辟的空间不再使用时,应该及时将其回收)

四、动态内存分配经典笔试题

补充:将常量字符串直接传给printf函数时,printf本质上接收的是常量字符串中首字符的地址

#include<stdio.h>
int main()
{printf("abcdef\n");//printf本质上接收的是字符串首字符的地址char* p = "abcdef";printf(p);return 0;
}

第一道

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void GetMemory(char* p)
{p = (char*)malloc(100);
}
//错误1:动态开辟了100个字节的空间,但是没有回收,因此会造成内存泄漏
void Test(void)
{char* str = NULL;GetMemory(str);//传值调用//将str的值传给了p,但GetMemory函数调用结束后,str的值仍然是NULLstrcpy(str, "hello world");//错误2:对空指针进行解引用,程序会崩溃printf(str);
}
int main()
{Test();return 0;
}

将上述代码修改正确

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

第二道(返回栈空间的地址的问题)

#include<stdio.h>
char* GetMemory(void)
{char p[] = "hello world";return p;
}
//将数组p首字符的地址返回给str后,数组就销毁了(局部变量出了作用域就会销毁),此时str就是野指针
void Test(void)
{char* str = NULL;str = GetMemory();printf(str);//随机值
}
int main()
{ Test();return 0;
}

第三道

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void Test(void)
{char* str = (char*)malloc(100);strcpy(str, "hello");free(str);//当str指向的动态开辟的空间被操作系统回收后,str就变成了野指针if (str != NULL){strcpy(str, "world");//对野指针解引用,造成了非法访问printf(str);}
}int main()
{Test();return 0;
}

五、柔性数组

①何为柔性数组?

结构体在创建时最后一个成员变量(前面至少还有一个成员变量)是大小未知的数组,该数组就被称为柔性数组。

struct S
{int a;int arr[0];//并不是指数组arr的大小是0个字节//arr就是柔性数组
};

有些编译器中,上述代码可能会报错,可以改为下面的代码

struct S
{int a;int arr[];//arr就是柔性数组
};

②sizeof在计算包含柔性数组的结构体类型大小时,会忽略柔性数组所占的大小。

#include<stdio.h>
struct S
{int a;int arr[];
};
//其实这个结构体类型的大小应该是大于4个字节的,但是sizeof在计算包含柔性数组的结构体类型大小时,会忽略柔性数组的大小
int main()
{printf("%zd\n", sizeof(struct S));//4return 0;
}

③包含柔性数组的结构体类型最好用malloc函数为其分配内存,并且分配的空间要足够大,以适应柔性数组的大小。

在这里插入图片描述

六、C/C++中程序中内存区域的划分

在这里插入图片描述


http://www.mrgr.cn/news/65802.html

相关文章:

  • Docker篇(容器的备份与迁移)
  • 详细分析Js中保留前几位小数的基本知识(附Demo)
  • Apache 配置出错常见问题及解决方法
  • word mathml 创建粗体字母快捷键
  • win10/11无休眠设置和断电后电池模式自动休眠而不是睡眠-用以省电
  • Facebook直播按钮缺失现象的深入分析
  • 使用 pytorch 运行预训练模型的框架
  • FFmpeg 4.3 音视频-多路H265监控录放C++开发十二:在屏幕上显示多路视频播放,可以有不同的分辨率,格式和帧率。
  • HTB:Shocker[WriteUP]
  • 如何在BSV区块链上实现可验证AI
  • 隆盛策略股票杠杆交易市场罕见,26只“牛股”提示风险
  • VSCode 1.82之后的vscode server离线安装
  • Centos使用yum获取离线安装包
  • springboot 单元测试-各个模块举例
  • 爱奇艺大数据多AZ统一调度架构:打破数据孤岛,提升效率
  • windows——病毒的编写
  • Fish Agent:集成 ASR 和 TTS 的端到端语音处理模型,支持多语言转换
  • 单体架构的 IM 系统设计
  • 【教学类-12-10】20241104《连连看竖版6*6 (3套题目空心图案)中2班
  • 泛微开发修炼之旅--53ecology表单转pdf源码修改相关(表单转pdf时可以修改最后生成的pdf的内容)
  • mysql5安装
  • 数字证书的简单记录
  • 基于SpringBoot司机信用评价的货运管理系统【附源码】
  • Windows无法访问\\192.168.1.156,错误代码0x800704cf
  • 11.4OpenCV_图像预处理习题02
  • Python 继承、多态、封装、抽象