C语言:动态内存(一篇拿捏动态内存!)

2023-11-17

目录

学习目标: 

为什么存在动态内存分配 

动态内存函数:

1. malloc 和 free

2. calloc

3. realloc

常见的动态内存错误:

1. 对NULL指针的解引用操作

2. 对动态开辟空间的越界访问

3. 对非动态开辟内存使用free释放

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

5. 对同一块动态内存多次释放

6. 动态开辟内存忘记释放(内存泄漏)

程序的内存开辟:

柔性数组:

柔性数组的使用:

柔性数组的优势:

 以上就是个人学习见解和学习的解析,欢迎各位大佬在评论区探讨!

感谢大佬们的一键三连! 感谢大佬们的一键三连! 感谢大佬们的一键三连!


学习目标: 

为什么存在动态内存分配?
动态内存函数的介绍:
1、malloc;
2、free;
3、calloc;
4、realloc;
5、常见的动态内存错误;
6、内存开辟;
6、柔性数组。

为什么存在动态内存分配 

一般的开辟空间的方式有两个特点:

1. 空间开辟大小是固定的。
2. 数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配
        由于对空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道, 那数组的编译时开辟空间的方式就不能满足了。 这时候就只能试试动态存开辟了。

动态内存函数:

1.malloc和free

void*  malloc (size_t size);
        
         size:内存块的大小(以字节为单位)。是无符号整型。 size_t
1.1 这个函数向内存申请一块 连续可用 的空间,并返回指向这块空间的指针。
1.2 如果开辟成功,则返回一个指向开辟好空间的指针
      如果开辟失败,则返回一个 NULL 指针,因此 malloc 的返回值 一定要做检查。
1.3 返回值的类型是 void* ,所以 malloc 函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。
1.4 如果参数 size 0 malloc 的行为是标准是未定义的,取决于编译器。

 C语言提供了另外一个函数free,专门是用来做动态内存的释放和回收,函数原型如下:

void  free (void* ptr);
        
         ptr:指向先前分配有的内存块的指针。
1.1 free 函数用来释放动态开辟的内存
1.2 如果参数 ptr 指向的空间不是动态开辟的,那 free 函数的行为是未定义的。
1.3 如果参数 ptr NULL 指针,则函数什么事都不做
1.4 malloc和free都声明在 stdlib.h 头文件中。
#include <stdio.h>

int main()
{
     //静态代码
     int num = 0;
     scanf("%d", &num);
     int arr[num] = {0};
     //动态代码
     int* ptr = NULL;
     ptr = (int*)malloc(num*sizeof(int));
     //判断ptr指针是否为空
     if(NULL != ptr)
     {
         int i = 0;
         for(i=0; i<num; i++)
         {
             *(ptr+i) = 0;
         }
     }
     //释放ptr所指向的动态内存
     free(ptr);
     ptr = NULL;
     return 0;
}

2. calloc

void* calloc (size_t num, size_t size);
        
        num:要分配的元素数。
        size:每个元素的大小。
2.1 函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且 把空间的每个字节初始化为0。
2.2 与函数 malloc 区别只在于 calloc 会在返回地址之前把申请的空间的每个 字节 初始化为全 0

#include <stdio.h>
#include <stdlib.h>
int main()
{
     int *p = (int*)calloc(10, sizeof(int));
     if(NULL != p)
     {
         //使用这块空间
     }
     free(p);
     p = NULL;
     return 0;
}

3.realloc

        有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的时候内存,我们一定会对内存的大小做灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小的调整。
void*  realloc (void* ptr, size_t size);
        
        ptr:指向先前分配有的内存块的指针。或者这可以是一个 空指针,在这种情况下,将分配一个新块(就像被调用一样)。
        size:内存块的新大小(以字节为单位)。是无符号整型。 size_t
3.1 ptr 是要调整的内存地址。
3.2 size 调整之后新大小。
3.3 返回值为调整之后的内存起始位置。
3.4 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到 的空间。
3.5 realloc 在调整内存空间的是存在两种情况:
        情况1 :原有空间之后有足够大的空间
                要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化。
        情况2 :原有空间之后没有足够大的空间
               原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小的连续空间来使用。这样函数返回的是一个新的内存地址。

#include <stdio.h>
int main()
{
     int *ptr = (int*)malloc(100);
     if(ptr != NULL)
     {
         //业务处理
     }
     else
     {
         exit(EXIT_FAILURE);    
     }
     //扩展容量
     //ptr = (int*)realloc(ptr, 1000);//这样可以吗?(如果申请失败会如何?)
 
     int*p = NULL;
     p = realloc(ptr, 1000);
     if(p != NULL)
     {
         ptr = p;
     }
     
     free(ptr);
     return 0;
}

常见的动态内存错误:

1. 对NULL指针的解引用操作

void test()
{
     int *p = (int *)malloc(INT_MAX/4);
     *p = 20;//如果p的值是NULL,就会有问题
     free(p);
}

2. 对动态开辟空间的越界访问

void test()
{
     int i = 0;
     int *p = (int *)malloc(10*sizeof(int));
     if(NULL == p)
     {
         exit(EXIT_FAILURE);
     }
     for(i=0; i<=10; i++)
     {
         *(p+i) = i;//当i是10的时候越界访问
     }
     free(p);
}

3. 对非动态开辟内存使用free释放

void test()
{
     int a = 10;
     int *p = &a;
     free(p);//ok?
}

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

void test()
{
     int *p = (int *)malloc(100);
     p++;
     free(p);//p不再指向动态内存的起始位置
}

5. 对同一块动态内存多次释放

void test()
{
     int *p = (int *)malloc(100);
     free(p);
     free(p);//重复释放
}

6. 动态开辟内存忘记释放(内存泄漏)

void test()
{
     int *p = (int *)malloc(100);
     if(NULL != p)
     {
         *p = 20;
     }
}
int main()
{
     test();
     return 0;
}

程序的内存开辟:

C/C++程序内存分配的几个区域:
1. 栈区(stack):在执行函数时,函数内 局部变量 的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 栈区主要存放运行函数而分配的 局部变量、函数参数、返回数据、返回地址等
2. 堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。分配方式类似于链表。
3. 数据段(静态区):(static)存放全局变量、静态数据。程序结束后由 系统释放
4. 代码段:存放函数体(类成员函数和全局函数)的二进制代码。

柔性数组:

typedef struct st_type
{
        int i ;
        int a []; // 柔性数组成员
} type_a ;
有些编译器会说上述定义错误,可改成:
typedef struct st_type
{
        int i ;
        int a [ 0 ]; // 柔性数组成员
} type_a ;
1.1 结构中的柔性数组成员前面必须至少一个其他成员。
1.2 sizeof 返回的这种结构大小不包括柔性数组的内存。
1.3 包含柔性数组成员的结构用 malloc () 函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。
typedef struct st_type
{
int i ;
int a [ 0 ]; // 柔性数组成员
} type_a ;
printf ( "%d\n" , sizeof ( type_a )); // 输出的是 4

柔性数组的使用:

int i = 0;
//这样柔性数组成员a,相当于获得了100个整型元素的连续空间。
type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));
//业务处理
p->i = 100;
for(i=0; i<100; i++)
{
     p->a[i] = i;
}
free(p);

柔性数组的优势:

第一个好处是: 方便内存释放
        如果我们的代码是在一个给别人用的函数中,你在里面做了二次内存分配,并把整个结构体返回给用户。用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free,所以你不能指望用户来发现这个事。所以,如果我们把结构体的内存以及其成员要的内存一次性分配好了,并返回给用户一个结构体指针,用户做一次free就可以把所有的内存也给释放掉。
第二个好处是: 这样有利于访问速度.
        连续的内存有益于提高访问速度,也有益于减少内存碎片(开辟的空间中间的间隔内存没有被利用)。

 以上就是个人学习见解和学习的解析,欢迎各位大佬在评论区探讨!

感谢大佬们的一键三连! 感谢大佬们的一键三连! 感谢大佬们的一键三连!

                                              

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C语言:动态内存(一篇拿捏动态内存!) 的相关文章

  • std::vector 的复制构造函数如何运行?

    一个如何std vector
  • OpenGL纹理渲染与原始不匹配

    我正在尝试使用 OpenGL 渲染纹理 我用作测试的纹理是白色背景上的一堆黑色矩形 如下所示 然而 在渲染时 纹理似乎被复制并叠加在其自身之上多次 我使用以下方法设置场景 std string vertexSource ShaderLoad
  • 如何在C编程中获取当前时间(以毫秒为单位)[重复]

    这个问题在这里已经有答案了 可能的重复 如何使用 ANSI C 测量以毫秒为单位的时间 https stackoverflow com questions 361363 how to measure time in milliseconds
  • 静态成员函数与C语言绑定?

    以下 C 代码可使用 Visual C 和 g 进行编译 struct S static void foo extern C void S foo struct T static void foo extern C void T foo a
  • C 中的分段错误

    我需要用 0 填充二维数组 但编译后的程序会出现此错误 怎么了 int main int vert 1001 1001 int hor 1001 1001 int dudiag 1416 1416 int uddiag 1416 1416
  • 将 LINQ to Entities 查询中的每个项目转换为接口的最佳方法是什么?

    我有一个实现 IUser 的实体对象 User IQueryable
  • 将内核链接到 PTX 函数

    我可以使用 PTX 文件中包含的 PTX 函数作为外部设备函数 将其链接到另一个应调用该函数的 cu 文件吗 这是另一个问题CUDA 将内核链接在一起 https stackoverflow com questions 20636800 c
  • CMake - 未定义参考

    我正在尝试将 gtest 包含到我的项目中 问题是我在 GTest 中收到未定义的引用错误 我正在尝试在 Gtest 中测试 Node 类 在节点的构造函数中 我使用类记录器 尽管我已将库记录器添加到 gtest target 中 但我仍然
  • F# 内联如何工作?

    对于 F 我的理解是您可以使用 inline 关键字在调用站点执行类型专门化 那是 val inline a gt b gt c when a or b static member a b gt c 约束条件是 a or b必须有一个静态成
  • DLL 中的 XP 风格组合框

    我需要使用 C 和 WIN32 API 无 MFC 在 DLL 中创建 XP 风格的组合框 我设法在 DLL 中创建控件 不是以 XP 风格 我设法在带有清单的 exe 中创建 XP 样式组合框 但它在 DLL 中不起作用 为了让您的 DL
  • Windows Phone HttpClient PostAsync 挂起且无响应

    我在拨打电话时遇到问题HttpClientWP 应用程序的 post 方法 PostAsync总是挂起并且不给出任何响应 当我从 WPF 应用程序中尝试时 相同的代码可以工作 这是我正在做的事情 服务器Web API代码 public cl
  • C++ std:.auto_ptr 或 std::unique_ptr (支持多个编译器,甚至是旧的 C++03 编译器)?

    我正在尝试更新一些 C 代码 我想转向更现代的代码 c 11 但我仍然需要使用一些较旧的编译器 兼容 c 03 来编译代码 因为支持的平台限制 我知道在 C 11 编译器中 std auto ptr 已被弃用 但由于较旧的编译器支持 我不能
  • MPI_Gatherv:根数组中收到的垃圾值

    我正在尝试实施MPI Gatherv函数于C 根据我的程序 包括 root 在内的每个进程都应该创建一个大小等于 进程的等级 1 这将在所有单元格中保持进程的等级 然后这个本地数组被收集到根的 rcv array 中 不知何故 我得到了垃圾
  • 将函数作为函数参数传递

    Unity C 似乎无法识别Func lt gt 作为函数委托的符号 那么 如何将函数作为函数参数传递呢 我有一个想法Invoke functionName 0 可能有帮助 但我不确定它是否实际上立即调用该函数 或者等待帧结束 还有别的办法
  • 使用std::begin()、std::end()将ArrayXd转换为stl向量,

    在我看来我应该能够使用std begin and std end 转换ArrayXd to std vector
  • RabbitMQ + Windows + LDAP 无需发送密码

    我正在尝试在 Windows 7 上使用 RabbitMQ 3 6 2 进行 LDAP 身份验证 授权 我已经在应用程序发送用户名 密码的情况下进行了基本身份验证 但密码位于我需要弄清楚如何进行的代码中避免 有没有人在不提供密码的情况下成功
  • Azure Function App Azure 服务总线触发器触发两次

    我使用带有服务总线触发器的 Azure Function Apps 来读取服务总线并对服务总线消息的内容执行操作 服务总线接收 JSON 序列化对象 然后将 JSON 消息反序列化回 Function App 中的对象 然而 由于某种原因
  • 在 C++ 中将大型数据向量写入/读取到二进制文件

    我有一个 C 程序 它通过将 ascii 文件中的网格人口数据读取到大型 8640x3432 元素双精度向量中来计算给定半径内的人口 将 ascii 数据读入向量大约需要 30 秒 循环每列和每行 而程序的其余部分只需要几秒钟 我被要求通过
  • 字符串常量之前应有非限定 ID

    我目前正在编写一个 C 应用程序 它与 math h 结合实现了振荡器 我拥有的代码应该可以很好地用于该应用程序 尝试编译目标文件 但是我遇到编译器错误 很可能与语法 等有关 我认为这与命名空间有关 错误 终端输出 User Name Ma
  • 散列 hash_hmac 时,Convert.ToChar(0) 散列结果与 PHP 中的 chr(0) 不同的字符串

    我在 PHP 中有一个字符串 它被转换为字节数组并进行哈希处理 转换为字节数组的字符串如下所示 G 字符 0 便便 我需要 C 中的等效字节数组 这样我才能得到相同的哈希值 编辑 这是完整的问题 生成的哈希值不同 PHP api secre

随机推荐

  • 数据库出现“评估期已过”问题怎么解决?

    问题 SQL server 2012年版 打开SQL Server时 出现问题 评估期已过 有关如何升级您的测试版软件的信息 请访问http www microsoft com sql howtobuy 问题原因 SQL Server安装时
  • Android Automotive概述

    Android开发者的新赛道 在智能手机行业初兴起时 包括BAT在内许多传统互联网企业都曾布局手机产业 但是随着手机市场的基本定型 造车似乎又成了各大资本下一个追逐的方向 百度 小米先后宣布造车 阿里巴巴则与上汽集团共同投资创立了 面向汽车
  • threejs-纹理贴图

    前言 threejs中的纹理贴图使用方法 参考 threejs开发指南 文章目录 前言 纹理的基本使用 纹理的常用属性 常用纹理的种类 1 普通贴图map 2 凹凸贴图bumpMap 3 法线贴图normalMap 4 位移贴图displa
  • 大数据量JSONObject.fromObject性能问题(大数据传给前台)

    最近项目中我负责了一个jms打印log信息的功能模块 大体需求是 用jms接受log信息 然后前台请求的时候 发给前台最新的log信息 前台会不断的刷新获取数据 个人思路是写一个静态的固定长度的list保存log信息 如果list满了清空
  • 通信端口感叹号_PCI简易通讯控制器有黄色感叹号怎么办?

    近日有网友新安装了Win7系统 安装完成后感觉电脑都顺畅了很多 不过在打开设备管理器的时候发现 在其他设备下的PCI简易通讯控制器有个黄色感叹号 出现这个感叹号也意味着这个控制器的驱动未安装 对此我们该如何解决呢 解决方法 1 打开设备管理
  • android 功能模块之通讯模块三

    Android通讯录开发之解决快速搜索联系人线程同步问题 2013年1月13日 上一篇博客介绍的是如何实现搜索 在PhoneUtil中已经定义好了search方法 开发者直接拿来用就ok了 但用的时候肯定会遇到线程同步问题 如何解决搜索的时
  • 区块链节点和网络的实现

    文章目录 1 介绍 1 1 区块链的基本概念回顾 1 2 区块链节点的作用和重要性 1 3 区块链网络的组成和结构 2 实现区块链节点 2 1 节点的角色和功能 2 2 使用Python创建区块链节点 2 3 定义区块和区块链数据结构 2
  • linux 内核笔记之watchdog

    watchdog 简而言之 watchdog是为了保证系统正常运行 或者从死循环 死锁等一场状态退出的一种机制 看门狗分硬件看门狗和软件看门狗 硬件看门狗是利用一个定时器电路 其定时输出连接到电路的复位端 程序在一定时间范围内对定时器清零
  • Vscode中JS输出乱码问题的解决

    一直很好用vscode突然不好用了 原来输出正常的JS代码在输出中都是乱码 于是上网查答案 试了很多奇奇怪怪的答案 然而没有一款能够解决我这个问题 仔细琢磨 既然以前好用 现在不好用 应该是某个电脑操作 误伤 友军 VScode执行代码原理
  • 五、Linux系统中的用户管理

    五 Linux系统中的用户管理 5 1 用户及用户组存在的意义 5 1 1 用户存在的意义 系统资源是有限的 如何合理分配系统资源 在这个问题解决时必须要有连个资源配合 1 身份 account 2 授权author 3 认证auth 3A
  • JS算法--整数反转

    1 需求介绍 给出一个 32 位的有符号整数 需要将这个整数中每位上的数字进行反转 注意 假设我们的环境只能存储得下 32 位的有符号整数 则其数值范围为 231 231 1 请根据这个假设 如果反转后整数溢出那么就返回 0 反转什么鬼 举
  • matlab 生成不重复的随机整数 打乱数据排列 生成深度学习数据集

    Matlab自带函数randperm n 可以产生1到n的整数的无重复的随机排列 利用它就可以得到无重复的随机数 例如 randperm n 产生一个1到n的随机顺序 gt gt randperm 10 ans 6 4 8 9 3 5 7
  • 人脸识别正则化系列之normface

    今天介绍一下NormFace L2 Hypersphere Embedding for Face Verification Motivation 希望利用正则化解决两个问题 1 人脸识别任务里面的loss有softmax contrasti
  • 【四】3D Object Model之创建Creation——read_object_model_3d()算子

    欢迎来到本博客 Halcon算子太多 学习查找都没有系统的学习查找路径 本专栏主要分享Halcon各类算子含义及用法 有时间会更新具体案例 具体食用方式 可以点击本专栏 Halcon算子快速查找 gt 搜索你要查询的算子名称 或者点击Hal
  • 9 款值得您花钱的最佳 PDF 编辑器

    PDF 格式在 90 年代初一推出就开始流行 PDF 文件便于携带 易于共享 阅读有趣 但难以编辑 有什么不喜欢的呢 与其他格式相比 无论大小的企业都更喜欢 PDF 因为他们不喜欢其他人篡改他们的文档 无论是指南 职业道德还是展示新产品功能
  • MySQL数据库基础操作—DML

    文章目录 DML的基本介绍 1 1数据插入 1 2数据修改 1 3 数据删除 注意 DML的基本介绍 DML是指数据库操作语言 全称是Data Manipulate Language 作用是对数据库中表的数据记录进行更新 关键字 插入ins
  • 区块链技术的主要特征有哪些

    区块链技术的主要特征有 1 去中心化 2 开放性 3 独立性 4 安全性 5 匿名性 从本质上讲 区块链是一个共享数据库 存储于其中的数据或信息 具有不可伪造 全程留痕 公开透明和集体维护等特征 区块链技术的特征 去中心化 区块链技术不依赖
  • Element ui中menu组件(el-menu/el-menu-item/el-submenu/template) 层级结构和用法

    此篇文章写下的时间是2020年 所以如今Element UI都更新了不知道多少版了 肯定会有些许变化 请勿完全照搬照抄 虽然可能这部分代码没什么大的变动 但还是要以官方文档为准 此文仅仅是借鉴 理解具体思路 然后再按照官方的例子来应用到自己
  • 软件工程——第7章实现知识点整理

    本专栏是博主个人笔记 主要目的是利用碎片化的时间来记忆软工知识点 特此声明 文章目录 1 实现由哪两个部分组成
  • C语言:动态内存(一篇拿捏动态内存!)

    目录 学习目标 为什么存在动态内存分配 动态内存函数 1 malloc 和 free 2 calloc 3 realloc 常见的动态内存错误 1 对NULL指针的解引用操作 2 对动态开辟空间的越界访问 3 对非动态开辟内存使用free释