[C][动态内存分配][柔性数组]详细讲解

目录

  • 1.动态内存函数的介绍
    • 1.malloc
    • 2.free
    • 2.calloc
    • 4.realloc
  • 2.常见的动态内存错误
  • 3.C/C++程序的内存开辟
  • 4.柔性数组
    • 1.是什么?
    • 2.柔性数组的特点
    • 3.柔性数组的使用
    • 4.柔性数组的优势


1.动态内存函数的介绍

1.malloc

  • 函数原型void* malloc(size_t size)
  • 功能
    • malloc()向内存申请一块连续可用的空间,并返回指向这块空间的指针
  • 返回值
    • 如果开辟成功,则返回一个指向开辟好空间的指针
    • 如果开辟失败,则返回一个NULL指针
      • 因此malloc的返回值一定要做检查
  • 返回值的类型是void*,所以**malloc()并不知道开辟空间的类型**,具体在使用的时候使用者自己来决定(强制类型转换)
  • 如果参数size == 0malloc()的行为是标准是未定义的,取决于编译器

2.free

  • 函数原型void free(void* ptr)
  • 功能free()用来释放动态开辟的内存
  • 如果参数ptr指向的空间不是动态开辟的,那free()的行为是未定义的
  • 如果参数ptrNULL,则函数什么事都不做
  • 一次完整的动态内存开辟的例子
int* p = (int*)malloc(10 * sizeof(int));
if(p == NULL)
{perror("main"); // main: xxxxreturn 0;
}// 使用
for(int i = 0; i < 10; i++)
{*(p + i) = i;
}free(p); // 回收空间
p = NULL; // 手动把p置为NULL

2.calloc

  • 函数原型void* calloc(size_t num, size_t size)
  • 功能:为num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为0
  • 与函数malloc()的区别只在于calloc()会在返回地址之前把申请的空间的每个字节初始化为全0
  • 如果对申请的内存空间的内容要求初始化,那么可以很方便的使用calloc函数来完成任务

4.realloc

  • 函数原型void* realloc(void* ptr, size_t size)

  • 功能

    • realloc()的出现让动态内存管理更加灵活
    • 有时会发现过去申请的空间太小了,有时候又会觉得申请的空间过大了,一定会对内存的大小做灵活的调整,realloc()就可以做到对动态开辟内存大小的调整
  • 参数

    • ptr:要调整的内存地址
      • ptr == NULL,则realloc()作用同malloc()
    • size:调整之后新大小
  • 返回值:调整之后的内存起始位置

  • 注意:这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间

  • realloc()在调整内存空间时,存在两种情况

    1. 原有空间之后有足够大的空间
      • 要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化
    2. 原有空间之后没有足够大的空间
      • 扩展方法
        • 在堆空间上另找一个合适大小的连续空间来使用
        • 这样函数返回的是一个新的内存地址
    • 由于上述的两种情况,realloc()的使用就要注意一些
      请添加图片描述
  • 例如:为了确保内存空间成功开辟,拿一个临时指针去接收新开辟的空间的地址,再赋值,这样保证了万一没有成功开辟新的地址,而丢失了原来的地址

int* p =int*)calloc(10, sizeof(int));
if(p == NULL)
{perror("main");return 1;
}for(int i = 0; i < 10; i++) // 使用
{*(p + i) = i;
}// 这里需要p指向更大空间,realloc调整
int* ptr = (int*)realloc(p, 20 * sizeof(int));
if(ptr != NULL)
{p = ptr;
}free(p);
p = NULL;

2.常见的动态内存错误

  1. 对NULL指针的解引用操作
  2. 对动态开辟空间的越界访问
  3. 使用free释放非动态开辟的空间
  4. 使用free释放动态内存中的一部分
  5. 对同一块动态开辟的空间,多次释放
  6. 动态开辟的空间忘记释放- 内存泄漏 -> 比较严重
  • 经典例子一
    • str传给GetMemory()的时候是值传递,所以GetMemory()的形参pstr的一份临时拷贝
    • GetMemory()内部动态申请空间的地址,存放在p中,不会影响外边的str,所以当GetMemory()返回之后,str依然是NULL,所以strcpy()会失败
    • GetMemory()返回之后,形参p销毁,使得动态开辟的100个字节存在内存泄漏无法释放
void GetMemory(char* p)
{p = (char *)malloc(100);
}void Test(void)
{char* str = NULL;GetMemory(str);strcpy(str, "hello world");
}int main()
{Test();return 0;
}// 改进一
char* GetMemory(char* p)
{p = (char *)malloc(100);return p;
}void Test(void)
{char* str = NULL;str = GetMemory(str);strcpy(str, "SnowK");free(str);str = NULL:
}// 改进二
void GetMemory(char** p)
{*p = (char *)malloc(100);
}void Test(void)
{char* str = NULL;GetMemory(&str);strcpy(str, "SnowK");free(str);str = NULL:
}
  • 经典例子二
    • GetMemory()内部创建的数组是在栈区上创建的
    • 出了函数,p[]的空间就还给了操作系统
    • 返回的地址是没有实际的意义,如果通过返回的地址去访问内存,就是非法访问内存
char* GetMemory()
{char p[] = "SnowK";return p;
}void Test()
{char* str = NULL;str = GetMemory();
}

3.C/C++程序的内存开辟

  • C/C++程序内存分配的几个区域:
    1. 栈区(stack)
      • 在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放
      • 栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限
      • 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等
    2. 堆区(heap)
      • 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收
      • 分配方式类似于链表
    3. 数据段(静态区)(static)
      • 存放全局变量、静态数据程序
      • 结束后由系统释放
    4. 代码段:存放函数体(类成员函数和全局函数)的二进制代码
      • 实际上普通的局部变量是在栈区分配空间的,栈区的特点是在上面创建的变量出了作用域就销毁
      • 但是被static修饰的变量存放在数据段(静态区),数据段的特点是在上面创建的变量,直到程序结束才销毁,所以生命周期变长
        请添加图片描述

4.柔性数组

1.是什么?

struct s
{int n;int arr[]; // 大小是未知
}

2.柔性数组的特点

  • 结构中的柔性数组成员前面必须至少一个其他成员
  • sizeof返回的这种结构大小不包括柔性数组的内存
  • 包含柔性数组成员的结构用malloc()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小
typedef struct st_type
{int i;int arr[0]; // 柔性数组成员
}type_a;printf("%d\n", sizeof(type_a)); // 输出的是4

3.柔性数组的使用

// 期望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;

4.柔性数组的优势

  • 方便内存释放
    • 如果代码是在一个给别人用的函数中,你在里面做了二次内存分配,并把整个结构体返回给用户
    • 用户调用free()可以释放结构体,但是用户并不知道这个结构体内的成员也需要free(),所以你不能指望用户来发现这个事
    • 所以,如果把结构体的内存以及其成员要的内存一次性分配好了,并返回给用户一个结构体指针,用户做一次free()就可以把所有的内存也给释放掉
  • 这样有利于访问速度
    • 连续的内存有益于提高访问速度,也有益于减少内存碎片
      • 其实,个人觉得也没高多少,反正也避免不了要用做偏移量的加法来寻址

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/18481.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【PB案例学习笔记】-11动画显示窗口

写在前面 这是PB案例学习笔记系列文章的第11篇&#xff0c;该系列文章适合具有一定PB基础的读者。 通过一个个由浅入深的编程实战案例学习&#xff0c;提高编程技巧&#xff0c;以保证小伙伴们能应付公司的各种开发需求。 文章中设计到的源码&#xff0c;小凡都上传到了gite…

颈源性头痛症状及表

颈源性头痛一般表现为&#xff0c;就是说从枕后一直颞侧&#xff0c;到太阳穴附近&#xff0c;这个是枕小的一个疼痛&#xff0c;还有一部分人从枕后&#xff0c;沿着一个弧线&#xff08;如下图&#xff09;的轨迹到了前额&#xff0c;到我们前额&#xff0c;这样一个疼痛&…

ADIL简单测试实例

参考资料&#xff1a;https://blog.csdn.net/geyichongchujianghu/article/details/130045373这个连接是Java的代码&#xff0c;我根据它的链接写了一个kotlin版本的。 AIDL&#xff08;Android Interface Definition Language&#xff09;是Android平台上用于进程间通信&…

AI办公自动化:kimi批量新建文件夹

工作任务&#xff1a;批量新建多个文件夹&#xff0c;每个文件夹中的年份不一样 在kimi中输入提示词&#xff1a; 你是一个Python编程专家&#xff0c;要完成一个编写关于录制电脑上的键盘和鼠标操作的Python脚本的任务&#xff0c;具体步骤如下&#xff1a; 打开文件夹&…

FFmpeg编解码的那些事(1)

看了网上很多ffmpeg的编解码的文章和代码&#xff0c;发现有很多文章和代码都过时了&#xff0c;主要还是ffmpeg有很多接口都已经发生变化了。 这里简单说一下&#xff0c;什么是编码和解码。 1.视频编码 对于视频来说&#xff0c;可以理解为多张&#xff08;rgb或者yuv&…

Python散点图矩阵代码模版

本文分享Python seaborn实现散点图矩阵代码模版&#xff0c;节选自&#x1f449;嫌Matplotlib繁琐&#xff1f;试试Seaborn&#xff01; 散点图矩阵&#xff08;scatterplot matrix&#xff09;展示原始数据中所有变量两两之间关系&#xff0c;可以规避单一统计指标的偏差&…

Neural Filters:照片恢复

Ps菜单&#xff1a;滤镜/Neural Filters/恢复/照片恢复 Neural Filters/RESTORATION/Photo Restoration 照片恢复 Photo Restoration借助 AI 强大功能快速恢复旧照片&#xff0c;提高对比度、增强细节、消除划痕。将此滤镜与着色相结合以进一步增强效果。 “照片恢复”滤镜利用…

Scikit-Learn随机森林

Scikit-Learn随机森林 1、随机森林1.1、集成学习1.2、Bagging方法1.3、随机森林算法1.4、随机森林的优缺点2、Scikit-Learn随机森林回归2.1、Scikit-Learn随机森林回归API2.2、随机森林回归实践(加州房价预测)1、随机森林 随机森林是一种由决策树构成的集成算法,它在大多情况…

mac安装的VMware虚拟机进行桥接模式配置

1、先进行网络适配器选择&#xff0c;选择桥接模式 2、点击网络适配器 设置... 3、选择WiFi&#xff08;我使用的是WiFi&#xff0c;所以选择这个&#xff09;&#xff0c;注意看右边的信息&#xff1a;IP和子网掩码&#xff0c;后续配置虚拟机的ifcfg-ens文件会用到 4、编辑if…

【论文阅读笔记】The Google File System

1 简介 Google File System (GFS) 是一个可扩展的分布式文件系统&#xff0c;专为快速增长的Google数据处理需求而设计。这篇论文发表于2003年&#xff0c;此前已在Google内部大规模应用。 GFS不仅追求性能、可伸缩性、可靠性和可用性等传统分布式文件系统的设计目标&#xf…

P9 【力扣+知识点】【算法】【二分查找】C++版

【704】二分查找&#xff08;模板题&#xff09;看到复杂度logN&#xff0c;得想到二分 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值存在返回下标&#xff0…

企业微信hook接口协议,ipad协议http,语音转文字

语音转文字 参数名必选类型说明uuid是String每个实例的唯一标识&#xff0c;根据uuid操作具体企业微信msgid是int要转文字的语音消息id 请求示例 {"uuid":"a4ea6a39-4b3a-4098-a250-2a07bef57355","msgid":1063645 } 返回示例 {"data&…

电源模块测试系统怎么测试输入电压范围?

在现代电子设备中&#xff0c;电源模块的性能直接影响着整个系统的稳定性和效率。其中&#xff0c;电源输入电压范围是指电源能够接受的输入电压的最小值和最大值&#xff0c;它是确保电源正常工作的重要参数。为了提高测试效率和精度&#xff0c;自动化的测试方法逐渐取代了传…

【Game】Rumble Heroes

文章目录 1 英雄2 守护兽3 符文4 祝福5 阵容推荐6 Boss7 兑换码 1 英雄 &#xff08;1&#xff09;力量 神话英雄 圣骑士-乌瑟尔 传说英雄 双刀-宫本武藏死亡骑士-阿萨斯冰霜骑士-亚瑟疾风焰刃-缘壹熊猫武僧-阿宝 史诗英雄 大剑-克劳德狂战士-奎托斯魔山-克里刚猎人-奈辛瓦里 稀…

宝塔部署Java+Vue前后端分离项目

1. 服务器 服务器选择Linux的CentOS7的版本 2. 宝塔Linux面板 2.1 百度搜索宝塔 2.2 进去之后点击立即免费安装 2.3 选择Linux在线安装&#xff0c;输入服务器信息进行安装(也可以选择其他方式) 安装完成之后会弹一个宝塔的应用面板&#xff0c;并附带有登录名称和密码&…

多模态大模型:系统、趋势与问题

引言 多模态大模型是当今人工智能领域的热门方向之一。它不仅能处理文本&#xff0c;还能理解和生成图像、视频、语音等多种模态的数据。这种能力使得多模态大模型在自然语言处理、计算机视觉等多个领域展示出巨大的潜力和应用价值。那么&#xff0c;多模态大模型是如何训练出…

AI菜鸟向前飞 — LangChain系列之十五 - Agent系列:从现象看机制(中篇)一个Agent的“旅行”

Agent基本架构 先谈谈Agent基本架构概念&#xff0c;如果看得云里雾里&#xff0c;等看完本篇之后&#xff0c;再回头看就会豁然开朗的&#xff0c;而我尽量写得更易懂&#xff1a; &#xff09; 这里面会穿插着上一篇的内容&#xff0c;请大家记得往回翻翻&#xff0c;传送门&…

C语言 | Leetcode C语言题解之第118题杨辉三角

题目&#xff1a; 题解&#xff1a; int** generate(int numRows, int* returnSize, int** returnColumnSizes) {int** ret malloc(sizeof(int*) * numRows);*returnSize numRows;*returnColumnSizes malloc(sizeof(int) * numRows);for (int i 0; i < numRows; i) {re…

前端API: IntersectionObserver的那一二三件事

IntersectionObserver 基础 IntersectionObserver 可以监听一个元素和可视区域相交部分的比例&#xff0c;然后在可视比例达到某个阈值的时候触发回调。比如可以用来处理图片的懒加载等等 首先我们来看下基本的格式&#xff1a; const observer new IntersectionObserver(c…

yolov10 使用自己的数据集训练目标检测模型

1 环境配置(使用anaconda) conda create -n yolov10 python=3.9 //创建虚拟环境 conda activate yolov10 //激活虚拟环境 pip install -r requirements.txt //执行yolov10 路径下requirements.txt 安装依赖 pip install -e .2.数据集制作 使用lableImage制作数据集(win版…