当前位置:网站首页>动态内存精讲
动态内存精讲
2022-07-21 12:45:00 【缘友一世】
前言
这便文章是知识型🧣🧣🧣的文章,小编认为不仅要做到知识的分享还要给枯燥的知识内容🧩🧩🧩,加上一些有趣的东西
如果您认可小编的努力,希望得到您的一个赞
flag:键盘敲烂,月薪过万。东方不败,唯我独尊
- 首先开始之前,欣赏一张图片
当然不是这张了,是下面这张 - 心之所向,素履以往;生如逆旅,一苇以航。
动态内存开辟
首先学四个动态内存函数
是不是回到初中的感觉,或者高中,一入编程深似海,函数从此成堆来。
一对亲兄弟–malloc和free
大哥:malloc
void * malloc(size_t size);
人物 | 简介 |
---|---|
malloc | 向内存申请一块连续可用的空间,并返回指向这块空间的指针 |
战斗力 | 开辟成功——返回一个指向开辟好的空间的地址 |
开辟失败——返回一个NULL【啥也没有,啥也不是】 | |
特点 | 返回的类型是void* ,malloc函数不知道开辟空间的类型,在具体使用的时候自己决定【就好像熊孩子自己干了坏事,自己都事情的严重型,只有发现时才能定性后果的严重】 |
当然不要忘记了熊孩子的父母——头文件
#include<stdlib.h>
- 大哥武艺展示
#include<stdio.h>
#include<stdlib.h>
int main(){
//动态开辟内存
int* p=(int*)malloc(10*sizeof(int));//也可以写成数字但是不推荐
if(p==NULL)
{
perror("main");
return 0;
}
for(int i=0;i,10;i++)
{
//结果为随机值
printf("%d\n",p[i]);//p[i]==*(p+i)
}
free(p);//回收空间
p=NULL;//手动设置p为NULL
}
如果开辟的空间过大会开辟失败:mian(Not enough space)
四弟:free
- 为什么时四弟?,请继续阅读
- 四弟真身
void fre(void * ptr);
人物 | 简介 |
---|---|
free | 用来释放动态开辟的内存 |
异常 | 如果ptr指向的空间不是动态开辟的,free函数的行为是未定义的 |
如果ptr是NULL指针,则函数什么都不做 |
calloc二弟
void * calloc(size_t num,size_t size);
Allocates an array in mamory with elements initialized to 0
翻译的意思就是:会初始化内存中的数组为0
- 二弟秀时刻
#include<stdio.h>
#include<stdlib.h>
int main(){
int* p=(int* )calloc(10,sizeof(int));
if(p==NULL)
{
perror("main");
return 1;
}
for(int i=0;i<10;i++)
{
printf("%d",p[i]);
}
free(p);
p=NULL;
}
realloc小三【最强挂王】
void* reallic(void* ptr,size_t size);
ptr——要调整的内存大小
size—— 调整之后新大小
返回值是调整之后的内存的起始位置
在调整原内存空间的基础上,还会将原来内存中的数据 移动到新的空间
- 挂王简介:
- 灵活管理动态内存
- 可以对申请的内存大小进行动态的调整
- 可能的情况
- 跟大哥单挑–动态开辟功能
int main()
{
//类似于malloc,直接在堆区开辟内存
int *p=(int* )realloc(NULL,10*sizeof(int));
}
常见的动态内存(bug)错误
1 对NULL的接引用操作
void test()
{
int*p=(int*)malloc(INT_MAX/4);
*p=20;//如果开配失败,p是空指针,就会有问题
free(p);
}
2 对动态内存开辟空间的越界访问
#include<stdlib.h>
int main()
{
int *p=(int *)malloc(10*sizeof(int));
if(p==NULL)
return 1;
for(int i=0;i<20;i++)
{
p[i]=i;//i>9之后是越界的
}
free(p);
p=NULL;
return 0;
}
3 使用free释放非动态内存开辟的空间
#include<stdlib.h>
int main() {
int arr[10]={
0};//栈区
int * p=arr;
free(p);//使用free释放非动态内存开辟的空间
p=NULL;
return 0;
}
4 使用free释放动态内存的一部分
void test()
{
int* p=(int *)malloc(100);
p++;
free(p);//p不在指向动态内存的起始位置
int *p=(int *)malloc(10* sizeof(int));
if(p=NULL)
return 1;
for(int i=0:i<2;i++)
{
*p++=i;
}
free(p);
p=NULL;
return 0;
}
5 对同一块动态开辟的空间,多次释放
int main()
{
int*p=(int*)malloc(10);
free(p);
free(p);
return 0;
}
- 解决方法一:将第二次的释放删去
但是有时候代码太长,很复杂,不想搞了 - 解决方法二:p置0
int main()
{
int*p=(int*)malloc(10);
free(p);
p=NULL;
free(p);//在第一次释放p之后,使p指向NULL;即使再次释放,free(NULL)不做任何操作
return 0;
}
6 动态开辟的空间忘记释放——内存泄漏
void test()
{
//p是在栈区
//malloc()在堆区开辟
int *p=(int*)malloc(100);
if(p!=NULL)
{
*p=20;
}
//未释放内存
}
int main() {
test();
while(1);
}
这个我来讲一个故事:
p警长安排malloc警员去犯罪集团中做卧底。【text函数中动态开辟内存】
只有他们两个人知道这件事,两人保持单线联系。一年后p警长不幸牺牲【text函数执行完成,释放栈区内存的内容】
警局再也找不到malloc了,但是还要给malloc薪水。【无法释放堆区内存,但程序不结束,仍然占用内存】
- 动态开辟的空间,2种回收的方式
- 主动free
- 程序结束,自动释放
相关错误练习
错误1:内存泄漏
#include<string.h>
#include<stdio.h>
#include<stdlib.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;
}
解决方法一:返回p指针
#define _CRT_SECURE_NO_WARNINGS
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
char* GetMemory(char* p)
{
p = (char*)malloc(100);
return p;
}
void Test()
{
char* str = NULL;
str=GetMemory(str);
strcpy(str,"hello world");
printf(str);
free(str);
str = NULL;
}
int main()
{
Test();
return 0;
}
解决方法二:地址传递
- 接收指针的地址,p是二级指针
- *p:对二级指针p解引用得到一级指针str的地址,使str指向堆区
#define _CRT_SECURE_NO_WARNINGS
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
void GetMemory(char** p)
{
*p = (char*)malloc(100);
return p;
}
void Test()
{
char* str = NULL;
GetMemory(&str);
strcpy(str,"hello world");
printf(str);
free(str);
str = NULL;
}
int main()
{
Test();
return 0;
}
错误二:非法访问
char* GetMemory()
{
char p[]="Hello world";
return p;
}
void Test()
{
char* str=NULL;
str=GetMemory();
printf(str);
}
int main()
{
Test();
return 0;
}
- GetMemory函数内部创建的数组是在栈区创建的
- 出了函数,p数组的空间就还给了操作系统
- 返回的地址是没有实际的意思,如果通过返回的地址,去访问内存就是非法访问
错误3:野指针
int* f()
{
int * ptr;//野指针未初始化
*ptr=10;
return ptr;
}
错误4:未释放
void Getmemory(char** p,int num)
{
*p=(char*)malloc(num);
}
vloid Test()
{
char* str=NULL;
GetMemory(&str,100);
strcpy(str,"Hello");
printf(str);
//添加的解决释放代码
free(p);
p=NULL;
}
int main()
{
Test();
return 0;
}
错误5:释放后的非法访问
void Test()
{
char* str=(char*)malloc(100);
strcpy(str,"hi");
free(str);//释放了堆区的内存,但是str中还保存着地址,但是使用的权限归操作系统,没权访问
if(str!=NULL)
{
strcpy(str,"hello");//错误,非法访问
printf(str);
}
}
int main()
{
Test();
return 0;
}
- C/C++程序内存分配的几个区域:
- 栈区(stack):在执函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。栈区主要存放运行函数而分配的局部变量、函数参数、返回数据返回地址等
- 堆区(heap):一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。分配方式类似于链表
- 数据段(静态区static)存放全局变量、静态数据。程序结束后由系统释放
- 代码段:存放函数体(类成员函数和全局函数)的二进制代码
static
- 普通的局部变量是在栈区分配空间的,栈区的特点是在上面创建的变量出了作用域就销毁。
- 但是被static修饰的变量存放在数据段(静态区),数据段的特点是在上面创建的变量,直到程序结束才销毁
- 生命周期变长
柔性数组
- 结构体中最后一个元素是未知大小的数组,叫柔性数组。
两种形式:
typedef struct st_type
{
int i;
int a[0];//柔性数组成员
} type_a;
typedef struct st_type
{
int i;
int a[];//柔性数组成员
} type_a;
举例:柔性数组
struct s
{
int n;
int arr[0];//大小未知
}
int main()
{
//期盼arr的大小是10个整数
struct S* ps=(struct S*)malloc(sizeof(struct S)+10*sizeof(int));
ps->n=10;
for(int i=0;i<10;i++)
{
ps->arr[i]=i;
}
//增加
struct S* ptr=(struct S*)realloc(ps,sizeof(struct S)+20*sizeof(int));
if(ptr!=NULL)
{
ps=ptr;
}
//使用
free(ps);
ps=NULL;
return 0;
}
同效果的非柔性数组的代码
struct S
{
int n;
int* arr;
}
int mian()
{
struct S* ps=(struct S*)malloc(sizeof(struct S));
if(ps==NULL)
reurn 1;
ps->n=10;
ps->arr=(int*)malloc(10*sizeof(int));
if(ps->arr==NULL)
{
return 1;
}
for(int i=0;i<10;i++)
{
ps->arr[i]=i;
}
//增加
int* ptr=relloc(ps->arr,20*sizeof(int));
if(ptr!=NULL)
{
ps->arr=ptr;
}
free(ps->arr);
ps->arr=NULL;
free(ps);
ps=NULL;
reuturn 0;
}
对比:
- 柔性数组需要释放的指针少,不容易出错
- 频繁地申请空间,增加了内存碎片,降低了效率,降低了内存占有率
- 第一个好处是:方便内存释放
如果我们的代码是在一个给别人用的函数中,你在里面做了二次内存分配并把整个结构体返回给用户。用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free,所以你不能指望用户来发现这个事。所以,如果我们把结构体的内存以及其成员要的内存一次性分配好了,并返回给用户一个结构体指针,用户做一次free就可以把所有的内存也给释放掉。- 第二个好处是:这样有利于访问速度
连续的内存有益于提高访问速度,也有益于减少内存碎片
不相关的知识:define与typedef的区别
#define INT_PTR int*
typedef int* int_ptr;
INT_PTR a,b;//int* a,b==int*a,(int)b;
int_ptr c,d//int*c,d==int*c,*d;
问题:那个变量不是指针?
答案:b
- define只有替换的作用:
- typedef是定义了一种数据类型:
边栏推荐
- 巴比特 | 元宇宙每日必读:要求高级政府官员披露其在NFT上的所有投资?美国政府道德办公室发布的这份法律咨询还说了什么?...
- Mysql-视图-存储过程与函数
- Cesium loads 3D tiles data
- [chestnut sugar GIS] ArcMap - how to quickly generate the four directions information
- C语言学生成绩管理系统
- Apple lost 340million Yuan due to bad keyboard. SpaceX received the order. Webb's successor, meta, sued meta. Today, more new things are here
- A concise tutorial for soft exam system architecture designers | software development model
- QT use qtreeview to login QQ friends list
- Yiyang Qianxi was scolded for being hot search...
- DRF -- jwt2 user authentication user defined control simpjwt return content
猜你喜欢
优秀的 Verilog/FPGA开源项目介绍(二十九)- 开源网站
A little cool, use Net Maui exploring space
Mysql-视图-存储过程与函数
MySQL view stored procedures and functions
[chestnut sugar GIS] ArcMap - how to quickly generate the four directions information
Deeply understand the timeout setting of istio traffic management
Musk uploaded that his brain was crazy, but neuralink was already a chicken feather
Let Matplotlib and Seaborn data map move ~
HCIP笔记整理 2022/7/14
DRF--users用户模块
随机推荐
SDL2 简明教程(一):使用 Cmake 和 Conan 构建 SDL2 编程环境
Go system monitoring
开源GIS体系
Mid year inventory | in 2022, PAAS will be upgraded again
Verification of implicit conversion rules for different types of arithmetic operations in C language
Summary of information retrieval and search skills of Baidu /google and other search engines
Anti counterfeiting AI ownerless lamp, who is playing it really?
归并排序思路及例题
HCIP笔记整理 2022/7/17
Model/view architecture of QT
Research on decompilation of wechat applet
Qt之Model/View架构
MySQL view stored procedures and functions
SDL2 简明教程(三):显示图片
Summary of related concepts of Nacos
[low code] 1387 try to understand the essence of low code platform design from another angle
七月集训(第21天) —— 堆(优先队列)
If there is out in PG function, returns setof record as is ignored$$
牛客网发布了全新数字逻辑题库!会不会导致今年FPGA/IC行业更卷?!!
Lianying medical passed the registration: it plans to raise 12.5 billion yuan, and Xue min controls 32% of the equity