首页 > 系统相关 >C语言——动态内存管理

C语言——动态内存管理

时间:2022-10-28 22:32:17浏览次数:54  
标签:malloc NULL 管理 int free C语言 动态内存 include 开辟

一.为什么存在动态内存分配

//局部变量
int a = 2;//在栈区开辟4个字节
char arr[10] = {0};//在栈区开辟10个字节的连续的空间

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

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

不过对于空间的开辟,有时我们需要想随便开辟多大空间的时候,就需要用到动态内存分配

C语言——动态内存管理_内存空间


二.动态内存函数的介绍

头文件均为   <stdlib.h>

malloc(开辟动态内存空间)

C语言——动态内存管理_内存空间_02

  • 开辟空间成功时,返回指向函数分配的内存块的指针。
  • 开辟空间失败时,则返回一个空指针(NULL)。因此malloc函数的返回值一定要做检查
  • 返回值的类型是void* ,具体情况下进行强制类型转换
  • malloc函数和free函数应一块使用

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
int main()
{
//想内存申请10个整形的空间
int* p = (int*) malloc(40);//void*强制类型转换为int*
if(p == NULL)//开辟失败
{
printf("%s\n",strerror(errno));//打印错误原因
}
else//开辟成功
{
int i = 0;
for(i = 0; i<10;i++)
{
*(p + i) = i;
printf("%d\n",*(p + i));
}
}
return 0;
}


​free(释放动态内存空间)

   

C语言——动态内存管理_i++_03

  • 参数(ptr)为 指向以前使用 或 分配的内存块的指针
  • 使用时,得有free函数应一块使用
  • 如果参数 ptr 指向的空间不是动态开辟的,则free函数的行为是未定义的
  • 如果参数ptr为NULL指针,则free函数什么都不会做

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
int main()
{
//想内存申请10个整形的空间
int* p = (int*) malloc(10*sizeof(int));//void*强制类型转换为int*
//或int* p = (int*)calloc(40);
if(p == NULL)//开辟失败
{
printf("%s\n",strerror(errno));//打印错误原因
}
else//开辟成功
{
int i = 0;
for(i = 0; i<10;i++)
{
*(p + i) = i;
printf("%d\n",*(p + i));
}
}

free(p);
p = NULL;
return 0;
}

注意:

//当动态申请的空间不再使用的时候,就应该还给操作系统

//虽然程序结束后由于生命周期而自动会还给操作系统,但当我们后续还有,这回导致这白白浪费

//虽然free(p)释放了p的空间后,但p仍能找到这个空间,这是具有潜在危险的,所以我们可以直接将其置空


calloc(开辟动态内存空间)

C语言——动态内存管理_#include_04

  • 返回值是指向开辟的空间的指针
  • 开辟空间失败时,则返回一个空指针(NULL)
  • calloc与malloc参数用法不同;calloc会把空间的每个字节初始化为0,而malloc不会初始化
  • 使用时,得有free函数应一块使用

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
int main()
{
//想内存申请10个整形的空间
int* p = (int*)calloc(10,sizeof(int));//void*强制类型转换为int*
//或int* p = (int*)calloc(10,4);
if(p == NULL)//开辟失败
{
printf("%s\n",strerror(errno));//打印错误原因
}
else//开辟成功
{
int i = 0;
for(i = 0; i<10;i++)
{
printf("%d\n",*(p + i));
}
}

free(p);
p = NULL;
return 0;
}
//0 0 0 0 0 0 0 0 0 0


realloc(调整动态内存的大小)

C语言——动态内存管理_#include_05

realloc函数能让动态内存管理更加灵活,在发现过去申请的内存空间过小或过大,可以使用realloc函数进行大小的调整


  • 如果p指向的空间后有足够的内存空间可以追加,则直接追加,返回p
  • 如果p指向的空间后没有足够的内存空间可以追加,则realloc函数会重找一个能满足需求的内存空间,并将原来内存中的数据拷贝过来,谁放掉旧的空间,最后返回新的内存空间
  • realloc函数调整失败后会返回一个空指针,不能拿p直接接收realloc,得用一个新的变量来接收返回值
  • 使用时,得有free函数应一块使用

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
int main()
{
//想内存申请10个整形的空间
int* p = (int*)malloc(20);
if(p == NULL)//开辟失败
{
printf("%s\n",strerror(errno));//打印错误原因
}
else//开辟成功
{
int i = 0;
for(i = 0; i<5;i++)
{
*(p + i) = i;
printf("%d ",*(p + i));
}
}

int* p2 = (int*)realloc(p,40);
int i = 0;
for(i = 5;i < 10;i++);
{
printf("%d ",*(p2 + i));
}
//free(p);
//p = NULL;

return 0;
}


注意:

//当realloc第一个参数为NULL时,可以相当于malloc
int* p = (int*)realloc(NULL, 40);


三.常见的动态内存错误

对空指针的解引用操作

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
int main()
{
int* p = (int*) malloc(40);

//未对失败的情况下判定
//万一malloc失败了,p被赋值为NULL
//而下面的操作,都成了非法操作

int i = 0;
for(i = 0; i<10;i++)
{
*(p + i) = i;
}

free(p);
p = NULL;

return 0;
}

对动态内存开辟空间的越界访问

开辟动态内存空间后,后面进行操作时却超出这个空间的大小

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
int main()
{
int* p = (int*) malloc(40);//void*强制类型转换为int*
if(p == NULL)//开辟失败
{
printf("%s\n",strerror(errno));//打印错误原因
}
else//开辟成功
{
int i = 0;
for(i = 0; i<20;i++)// 进行越界访问了
{
*(p + i) = i;
}
}
return 0;
}

对非动态开辟内存使用free函数释放

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
int main()
{
int a = 10;//a在栈区
int *p = &a;
*p = 20;
free(p);//free作用在堆区,强行乱指会造成程序崩溃

return 0;
}

使用free释放一块动态开辟内存的一部分

由于使p的地址发生改变时,释放的不再是原来的初始地址

#include <stdio.h>
#include <stdlib.h>
#include<errno.h>
#include<string.h>
int main()
{
int* p = (int*)malloc(10*sizeof(int));
if(p == NULL)//开辟失败
{
printf("%s\n",strerror(errno));//打印错误原因
}

int i = 0;
for (i = 0; i < 5; i++)
{
*p++ = i;
}
free(p);
p = NULL;

return 0;
}

对一块动态内存进行多次释放


#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
int main()
{
int* p = (int*) malloc(40);//void*强制类型转换为int*
if(p == NULL)//开辟失败
{
printf("%s\n",strerror(errno));//打印错误原因
}
else//开辟成功
{
int i = 0;
for(i = 0; i<10;i++)// 进行越界访问了
{
*(p + i) = i;
}
}

free(p);

free(p);
p = NULL;

return 0;
}
  • 可以在一个free后紧跟着将其置空,后面再出现时不会有影响
free(p);
p = NULL;
free(p);

动态开辟内存忘记释放(内存泄露)

#include <stdio.h>
#include <stdlib.h>
void test()
{
int* p = (int*)malloc(100);
if(p != NULL)
{
*p = 20;
}
}
int main()
{
test();
while(1);

}

忘记释放不再使用的动态开辟的空间会造成内存泄露

————动态开辟的空间一定要进行正确的释放

注意:​

动态开辟的空间有2种回收方式:主动free   ,   程序结束

四.柔性数组

1.定义

在 C99 中,结构体中的最后一个元素允许是未知大小的数组,即柔性数组

struct a{
int b;
int arr[];//大小是未知的
};

//或

struct a{
int b;
int arr[0];//大小是未知的
};

2.特点

  •  结构体中的 柔性数组成员 的 前面 必须至少有一个其它成员
  • sizeof 计算这种结构体的大小 不包含柔性数组成员的
  • 包含柔性数组成员的结构体用 malloc 函数进行内存分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小
#include<stdio.h>
#include<stdlib.h>
struct A{
int b;
int arr[];//大小是未知的
};
int main()
{
//期待arr的大小是10个整形
//结构体的初始化
struct A* ps = (struct A*)malloc(sizeof(struct A ) + 10*sizeof(int));//前4个字节是给b的,后40个字节是给arr的
//由于后面的是通过malloc开辟的动态内存,大小是可以调整的
printf("%d",sizeof(ps));
free(ps);
ps = NULL;
return 0;
}

//4

标签:malloc,NULL,管理,int,free,C语言,动态内存,include,开辟
From: https://blog.51cto.com/u_15722538/5805289

相关文章

  • 初始C语言
     基本了解C语言的基础知识,对C语言有一个大概的认识。每个知识点就是简单认识,不做详细讲解重点:什么是C语言第一个C语言程序数据类型变量、常量字符串+转义字符+注释选择语句......
  • 初识c语言
    前言小伙伴们大家好,今天开始,我们即将开启c语言的正是学习,从初级到进阶,想必大家都听说过c生万物,可想而知c语言对于我们程序员的重要之处,从此篇博客开始将持续更新c语言的相关......
  • 能耗监测管理平台
    系统架构:产品采用“2+2+1”(即双服务器+双隔离主机系统+物理隔离数据通道控制系统)体系结构;具备数据通道控制系统证书;通过嵌入式数据通道控制系统隔离外部网络,而不是采用DMA......
  • c语言—自定义类型(结构体,枚举,联合)进阶篇—笔记
    个人觉得结构体相当于类,应该是比较实用的功能。1.结构体结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。structtag{member-list;}variable-l......
  • c语言(猜数字游戏)
    #include<stdlib.h>#include<time.h>#include<stdio.h>voidmenu(){printf("********************************\n");printf("*******1.play*******\n")......
  • Docker 网络管理--网络创建和通信
    一、网络驱动docker可以通过创建虚拟网卡,通过虚拟网卡转发到宿主机网卡和外部进行通信。除此之外,也可以不创建自己的虚拟网卡而是直接和宿主机共用网卡直接占用宿主机I......
  • 薪酬管理系统的作用是什么?
    薪酬管理是企业单位人力资源管理中的重要组成部分,薪酬管理是在组织发展战略指导下,对员工薪酬支付原则、薪酬策略水平、薪酬结构构成进行确定、分配和调整的动态管理过程。......
  • 企业应当如何制定薪酬管理制度
        在全球化发展不断推进的背景下,我国企业发展态势也在不断向国际靠拢,这对于企业发展来说是一种必然趋势,更是其自身发展所需要。薪酬管理制度作为企业人力资源管理......
  • 贝锐蒲公英:智能组网打造水利监控系统互联精细化管理方案
    在物联网时代,伴随城市化进程的不断演变,市民生活用水压力日渐突出。为了改善这一现状,借助信息技术为手段支撑,实现对水利设施的水资源实时精细化监控管理,是当前较为有效......
  • C语言笔记 | C与汇编
    函数通过栈传递参数平衡栈(函数调用约定)cdecl参数从右至左入栈调用者清栈stdcall参数从右至左入栈自身清栈fastcall:ecx和edx传前两个参数,剩下的参......