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

C语言学习笔记——动态内存分配

目录

  • 1 动态内存分配常用库函数
    • 1.1 free
    • 1.2 malloc
    • 1.3 calloc
    • 1.4 realloc
  • 2 常见的动态内存错误
    • 2.1 对NULL指针的使用
    • 2.2 越界访问
    • 2.3 对非动态开辟的空间进行释放
    • 2.4 对动态开辟的空间重复释放
    • 2.5 释放部分动态开辟的空间
    • 2.6 忘记释放动态开辟的内存空间
  • 3 柔性数组

1 动态内存分配常用库函数

1.1 free

free 的函数声明如下:

void free (void* ptr);

作用
将动态开辟的空间进行回收

参数部分
指向动态分配内存空间的指针,若该指针为空指针,则 free 不会做任何事情

返回值
该函数没有返回值

一般来说,在使用了 malloc 等函数开辟动态内存空间后,若这块空间不需要使用,就要用 free 函数来进行回收,并将指针置为空,防止内存泄露和野指针问题

1.2 malloc

malloc 的函数声明如下:

void* malloc (size_t size);

作用
向操作系统申请 size 个字节大小的连续内存空间

参数部分
要分配的空间大小,以字节为单位

返回值
如果空间开辟成功,会返回这块连续内存空间的首地址,用户可以根据自己的需要,将返回的 void* 类型的指针进行强制类型转换,但是如果开辟失败,会返回空指针,所以用户应该在使用 malloc 后,检查指针的值是否为空,防止对空指针进行使用

举例:向操作系统申请 20 个字节大小的空间用于存储 int 类型数据

#include <stdlib.h>
int main()
{int* p = (int*)malloc(sizeof(int) * 5); if (p == NULL) //防止对空指针进行使用{perror("malloc");return 1;}free(p); //回收开辟的空间p = NULL; //防止野指针return 0;
}

int 类型大小 4 个字节,乘以 5 就是 20 个字节,由于要存放 int 类型数据,所以返回的指针强制类型转换为了 int*
在这里插入图片描述

申请了空间就需要使用,那么怎么使用这块空间呢?
在使用动态开辟的内存空间时,可以当成数组来使用,通过数组的遍历方式来使用这块空间

举例:开辟20个字节的空间存放 1 2 3 4 5 并输出

#include <stdlib.h>
int main()
{int* p = (int*)malloc(sizeof(int) * 5);if (p == NULL){perror("malloc");return 1;}for (int i = 0;i < 5;++i){*(p + i) = i + 1; //*(p + i) = p[i]}for (int i = 0;i < 5;++i){printf("%d ", *(p + i)); }free(p);p = NULL;return 0;
}

图解:
在这里插入图片描述

1.3 calloc

callo 的函数声明如下:

void* calloc (size_t num, size_t size);

作用
开辟 num 个大小为 size 的空间,并初始化为全 0

参数部分
num指元素个数
size指一个元素的大小

返回值
开辟成功返回指向动态开辟空间的指针,开辟失败则返回空指针

举例:开辟20个字节的空间存放 1 2 3 4 5 并输出

int main()
{int* p = (int*)calloc(5, sizeof(int)); //开辟5个大小为4个字节的空间if (p == NULL){perror("malloc");return 1;}for (int i = 0;i < 5;++i){*(p + i) = i + 1; //*(p + i) = p[i]}for (int i = 0;i < 5;++i){printf("%d ", *(p + i)); }free(p);p = NULL;return 0;
}

1.4 realloc

realloc 的函数声明如下:

void* realloc (void* ptr, size_t size);

作用
如果 ptr 为空,则开辟 size 个大小的空间,单位是字节
如果 ptr 不为空,则将 ptr 所指向的空间扩张至 size 个大小

参数部分
ptr 为指向动态开辟空间的指针
size 为新空间大小

返回值
开辟成功返回空间首地址的指针,开辟失败返回空指针

realloc 在重新分配空间时使用的方法
第一种:原空间后方的空间余量够扩大到 size 个时,则直接向后扩张即可
比如说,现在已经动态分配了 20B 的内存空间,想要扩张至 40B,在已分配空间后方还有 20B,够扩张到 40B,那么,就会直接向后扩张
图解:
在这里插入图片描述
**第二种:**原空间后方的空间余量不够扩大到 size 个时,则需要另找一片空间,重新分配
比如,现在已经动态分配了 20B 的内存空间,想要扩张至 40B,但是已分配空间后方不够 20B,不够扩张到 40B,那么,就会寻找一片 40B 的空间进行分配,将原空间的数据复制到里面,返回指向新空间的指针
在这里插入图片描述

举例:开辟20个字节的空间存放 1 2 3 4 5 后,将其扩大到 40B,再存放 6 7 8 9 10 并输出

#include <stdlib.h>
#include <stdio.h>
int main()
{int* p = (int*)malloc(sizeof(int) * 5);if (p == NULL){perror("malloc");return 1;}for (int i = 0;i < 5;++i) //存放1 2 3 4 5{*(p + i) = i + 1; //*(p + i) = p[i]}int* p2 = (int*)realloc(p, 40);if (p2 == NULL) //检查新空间是否开辟成功{perror("realloc");return 1;}p = p2; //开辟成功再改变指针指向p2 = NULL;for (int i = 5;i < 10;++i){*(p + i) = i + 1;}for (int i = 0;i < 10;++i){printf("%d ", *(p + i));}free(p);p = NULL;return 0;
}

2 常见的动态内存错误

2.1 对NULL指针的使用

int main()
{int* p = (int*)malloc(INT_MAX * 2);*p = 10; //p若为NULL,不能解引用free(p);p = NULL;return 0;
}

为了防止这个问题,需要对指针进行检查

int main()
{int* p = (int*)malloc(INT_MAX * 2);if (p == NULL) //防止空指针问题{perror("malloc");return 1;}*p = 10; //p若为NULL,不能解引用free(p);p = NULL;return 0;
}

2.2 越界访问

int main()
{int* p = (int*)malloc(sizeof(int) * 5);if (p == NULL){perror("malloc");return 1;}for (int i = 0;i <= 5;++i) //只有5个元素,但是下标遍历到了6{*(p + i) = i;}free(p);p = NULL;return 0;
}

2.3 对非动态开辟的空间进行释放

int main()
{int arr[] = { 1,2,3,4,5 };free(arr); //arr不是动态开辟的空间return 0;
}

2.4 对动态开辟的空间重复释放

int main()
{int* p = (int*)malloc(sizeof(int) * 5);if (p == NULL){perror("malloc");return 1;}free(p);free(p);p = NULL;return 0;
}

为了防止这个问题,应该在第一次释放后,将指针置为空

int main()
{int* p = (int*)malloc(sizeof(int) * 5);if (p == NULL){perror("malloc");return 1;}free(p);p = NULL;free(p);return 0;
}

2.5 释放部分动态开辟的空间

int main()
{int* p = (int*)malloc(sizeof(int) * 5);if (p == NULL){perror("malloc");return 1;}p++;free(p);p = NULL;return 0;
}

2.6 忘记释放动态开辟的内存空间

int main()
{int* p = (int*)malloc(sizeof(int) * 5);if (p == NULL){perror("malloc");return 1;}return 0;
}

3 柔性数组

柔性数组是通过结构体来实现的,具体实现方法为:

  • 将结构体的最后一个成员设置为数组,该数组前一定要有一个或多个成员变量
  • 此数组不设大小
struct S
{int a;int arr[]; //柔性数组成员
};

当结构体内有柔性数组,使用 sizeof 在计算类型大小时,不会将柔性数组的大小计算入内,只会计算柔性数组之前成员的大小,因此就可以使用动态分配的方式指定数组的空间大小

举例:分配大小为 20B 的空间给柔性数组

struct S
{int a;int arr[]; //柔性数组成员
};int main()
{int* p = (int*)malloc(sizeof(struct S) + sizeof(int) * 5); //先分配柔性数组前成员变量的空间,再分配柔性数组的空间if (p == NULL){perror("malloc");return 1;}free(p);p = NULL;return 0;
}

图解:
在这里插入图片描述
如果想要更改柔性数组的大小,只需要使用 realloc 即可

举例:将 20B 的柔性数组变成 40B

struct S
{int a;int arr[]; //柔性数组成员
};int main()
{int* p = (int*)malloc(sizeof(struct S) + sizeof(int) * 5); if (p == NULL){perror("malloc");return 1;}int* p2 = (int*)realloc(p, sizeof(struct S) + sizeof(int) * 10); //更改为40Bif (p2 == NULL){perror("malloc");return 1;}p = p2;p2 = NULL;free(p);p = NULL;return 0;
}

图解:
假设后方有空间
在这里插入图片描述

http://www.lryc.cn/news/610756.html

相关文章:

  • 备忘录记事本 任务清单 html
  • 手动开发一个TCP服务器调试工具(一):基础知识与核心类接口
  • HTML 如何转 Markdown
  • 【qt5_study】2.使用Qt Designer构造UI界面(信号与槽)
  • 16核32G硬件服务器租用需要多少钱
  • 工业级 CAN 与以太网桥梁:串口服务器CAN通讯转换器深度解析(下)
  • 前端实用工具方法 —— 持续更新中...
  • GPT-5的诞生之痛:AI帝国的现实危机
  • 前端权限设计
  • 云手机的主要功能都包含哪些?
  • MoonBit 月兔 - 云和边缘计算 AI云原生编程语言及开发平台
  • LangChain入门:代理、链、索引
  • WIN QT libsndfile库编译及使用
  • 【教程】Unity AssetBundle 资源管理方法
  • STM32F407VET6学习笔记10:移植smallmodbus
  • 【LeetCode 热题 100】347. 前 K 个高频元素——(解法一)排序截取
  • Redis类型之String
  • 【npm 解决】---- TypeError: crypto.hash is not a function
  • GPS信号捕获尝试
  • 【机器学习深度学习】模型剪枝
  • Python包安全工程实践:构建安全可靠的Python生态系统
  • 【学习笔记】NTP时间同步验证
  • 期权定价全解析:从Black-Scholes到量子革命的金融基石
  • Linux 逻辑卷管理:LVM 原理与 Stratis、VDO 特性对比
  • 基于 Spring Boot 的小区人脸识别与出入记录管理系统实现
  • 力扣经典算法篇-43-全排列(经典回溯问题)
  • css3属性总结和浏览器私有属性
  • Python、Java、C#实现浮点型转换为转型
  • Mysql使用Canal服务同步数据->ElasticSearch
  • 电子秤利用Websocket做为Client向MES系统推送数据