【一天一个计算机知识】—— 【 C/C++ 内存管理与分布】

【一天一个计算机知识】—— 【 C/C++ 内存管理与分布】



⚡ CYBER_PROFILE ⚡
/// SYSTEM READY ///


[WARNING]: DETECTING HIGH ENERGY

🌊 🌉 🌊 心手合一 · 水到渠成

分隔符
>>> ACCESS TERMINAL <<<
[ 🦾 作者主页 ][ 🔥 C语言核心 ]
[ 💾 编程百度 ][ 📡 代码仓库 ]

---------------------------------------
Running Process: 100% | Latency: 0ms


索引与导读

🚩一、C/C++ 内存分布

我们先来看下面的一段代码和相关问题:

#include<stdio.h>#include<stdlib.h>// 【全局/静态区】.data:已初始化的全局变量int globalVar =1;// 【全局/静态区】.data:已初始化的静态全局变量(作用域限制在本文件)staticint staticGlobalVar =1;voidTest(){// 【全局/静态区】.data:已初始化的静态局部变量(生命周期为整个程序)staticint staticVar =1;// 【栈区】:局部变量,函数运行结束自动释放int localVar =1;// 【栈区】:局部数组int num1[10]={1,2,3,4};// 【栈区】:字符数组(注意:"abcd" 会被拷贝到栈上)char char2[]="abcd";// 【指针在栈区】,但指向的 "abcd" 字符串字面量在【常量区/代码段】constchar* pChar3 ="abcd";// 【指针在栈区】,指向【堆区】通过 malloc 分配的内存int* ptr1 =(int*)malloc(sizeof(int)*4);// 【指针在栈区】,指向【堆区】通过 calloc 分配并初始化的内存int* ptr2 =(int*)calloc(4,sizeof(int));// 【指针在栈区】,将 ptr2 原有的【堆区】内存进行扩充/移动int* ptr3 =(int*)realloc(ptr2,sizeof(int)*4);// 手动释放【堆区】内存free(ptr1);free(ptr3);}
在这里插入图片描述

在这里插入图片描述

🚩二、C语言的动态内存管理

🔗Lucy的空间骇客裂缝:C语言动态内存管理

💪C动态内存管理的面试考点

1)realloc的工作机制

voidTest(){int* p2 =(int*)calloc(4,sizeof(int));int* p3 =(int*)realloc(p2,sizeof(int)*10);free(p3 );}
  • 这里需要free(p2)
  • 不需要。 甚至绝对不能这样做
原因在于 realloc 的工作机制:如果 realloc 成功扩容,它会返回一个新的内存地址(可能是原地址,也可能是搬迁后的新地址)。如果返回了新地址,原内存块(p2)会自动被释放。如果你手动执行 free(p2),会导致“重复释放”(Double Free)的错误,这通常会引起程序崩溃

2)malloc/calloc/realloc的区别是什么?

函数功能描述初始化行为参数形式
malloc分配指定字节大小的内存。不初始化。内存里是随机的垃圾值。malloc(size_t size)
calloc分配 n 个元素的内存,并清零。自动初始化为 0calloc(n, size)
realloc调整已分配内存块的大小。不对新增空间进行初始化。realloc(ptr, new_size)

🚩三、C++ 动态内存管理

  • C语言内存管理方式在C++中可以继续使用
  • C++又提出了自己的内存管理方式:通过newdelete操作符进行动态内存管理

1)操作内置类型

C++中,内置类型(如 int, double, char 等)的动态内存分配主要通过 newdelete 运算符来实现

1.1)单个变量的分配和释放

  • 分配:new 类型 或 new 类型(初始值)
  • 释放:delete 指针变量
intmain(){// 1. 分配一个未初始化的 intint* p1 =newint;// 2. 分配并初始化为 100int* p2 =newint(100);// 使用完毕后必须释放内存delete p1;delete p2;// 良好的习惯:将指针置为空,防止野指针 p1 =nullptr; p2 =nullptr;return0;}

1.2)一维数组的分配与释放

处理数组时,需要使用方括号 []

  • 分配:new 类型[元素个数]
  • 释放:delete[] 指针变量 (注意:释放数组必须加 [],否则会导致未定义行为或内存泄漏)
voidmanageArray(){int size =5;// 分配一个长度为 5 的 int 数组int* arr =newint[size];// 初始化数组(C++11 之后也可以使用 new int[5]{1, 2, 3, 4, 5})for(int i =0; i < size;++i){ arr[i]= i *10;}// 释放数组内存delete[] arr;}

2)操作自定义类型

对于自定义类型(如 structclass),C++newdelete 不仅仅是分配内存,它们还负责调用构造函数和析构函数

  • 最核心的差异在于:自定义类型的生命周期管理包含了初始化(调用构造函数)清理(调用析构函数) 的过程
#include<iostream>usingnamespace std;classA{public:A(int a =0):_a(a){ cout <<"A():"<<this<< endl;}~A(){ cout <<"~A():"<<this<< endl;}private:int _a;};intmain(){ A* p1 =(A*)operatornew(sizeof(A)); A* p2 =newA(1);operatordelete(p1);delete p2;int* p3 =(int*)operatornew(sizeof(int));int* p4 =newint;operatordelete(p3);delete p4; A* p5 =(A*)operatornew(sizeof(A)*10); A* p6 =new A[10];operatordelete[](p5);delete[] p6;return0;}

3)operator new与operator delete函数

  • newdelete 是用户进行动态内存申请和释放的操作符,operator newoperator delete 是系统提供的全局函数
  • new 在底层调用 operator new 全局函数来申请空间,delete 在底层通过 operator delete 全局函数来释放空间

  • new 表达式 (new expression): 这是我们在代码中常用的 Type* p = new Type();。它是一个“全自动”过程,做了两件事:
    1. 调用 operator new 分配原始内存。
    2. 调用构造函数初始化对象。
  • operator new (函数): 这是一个类似于 malloc 的函数,它的唯一任务是分配一块指定大小的原始内存,不负责构造对象。

3.1)operator new详解

🔗Lucy的空间骇客裂缝:operator new官方解析
  • 原型:
void*operatornew(size_t size);
  • 工作原理
    • 参数: size 是需要分配的字节数(编译器会自动计算对象大小并传入)。
    • 返回值: 返回指向分配的内存起始地址的 void* 指针。
    • 失败处理: 默认情况下,如果分配失败,它不会返回 nullptr,而是抛出 std::bad_alloc 异常。
    • 自定义行为: 你可以重载全局或类特定的 operator new 来实现自己的内存池或审计逻辑

3.2)operator delete 详解

🔗Lucy的空间骇客裂缝:operator delete官方解析
  • 原型:
voidoperatordelete(void* ptr)noexcept;
  • 工作原理
    • 对应关系delete 表达式会先调用对象的析构函数,然后调用 operator delete 来释放内存。
    • 安全性:根据 C++ 标准,向 operator delete 传递 nullptr 是安全的,函数会直接返回而不执行任何操作。
    • 释放逻辑:在底层,它通常封装了 free() 或者系统底层的内存回收接口。
C++ 中,noexcept 是一个异常说明,它告诉编译器和程序员:这个函数保证不会抛出异常

通过上述两个全局函数的实现知道,operator new 实际也是通过 malloc 来申请空间,如果 malloc 申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。operator delete 最终是通过 free 来释放空间的


4)new和delete的实现原理

4.1)内置类型

如果申请的是内置类型的空间,newmallocdeletefree 基本类似,不同的地方是:
new/delete 申请和释放的是单个元素的空间,new[]delete[] 申请的是连续空间,而且 new 在申请空间失败时会抛异常,malloc 会返回 NULL


4.2)自定义类型

  • new 的原理
    1. 调用 operator new 函数申请空间
    2. 在申请的空间上执行构造函数,完成对象的构造
  • delete 的原理
    1. 在空间上执行析构函数,完成对象中资源的清理工作
    2. 调用 operator delete 函数释放对象的空间
  • new T[N] 的原理
    1. 调用 operator new[] 函数,在 operator new[] 中实际调用 operator new 函数完成 N 个对象空间的申请
    2. 在申请的空间上执行 N 次构造函数
  • delete[] 的原理
    1. 在释放的对象空间上执行 N 次析构函数,完成 N 个对象中资源的清理
    2. 调用 operator delete[] 释放空间,实际在 operator delete[] 中调用 operator delete 来释放空间

5)malloc/free和new/delete的区别

malloc/freenew/delete 的共同点是:都是从堆上申请空间,并且需要用户手动释放。不同的地方是:

  1. mallocfree 是函数,newdelete 是操作符
  2. malloc 申请的空间不会初始化,new 可以初始化
  3. malloc 申请空间时,需要手动计算空间大小并传递,new 只需在其后跟上空间的类型即可,如果是多个对象,[] 中指定对象个数即可
  4. malloc 的返回值为 void*,在使用时必须强转,new 不需要,因为 new 后跟的是空间的类型
  5. malloc 申请空间失败时,返回的是 NULL,因此使用时必须判空,new 不需要,但是 new 需要捕获异常
  6. 申请自定义类型对象时,malloc/free 只会开辟空间,不会调用构造函数与析构函数,而 new 在申请空间后会调用构造函数完成对象的初始化,delete 在释放空间前会调用析构函数完成空间中资源的清理释放

💻结尾— 核心连接协议

警告:🌠🌠正在接入底层技术矩阵。如果你已成功破解学习中的逻辑断层,请执行以下指令序列以同步数据:🌠🌠


【📡】 建立深度链接:关注本终端。在赛博丛林中深耕底层架构,从原始代码到进阶协议,同步见证每一次系统升级。

【⚡】 能量过载分发:执行点赞操作。通过高带宽分发,让优质模组在信息流中高亮显示,赋予知识跨维度的传播力。

【💾】 离线缓存核心:将本页加入收藏。把这些高频实战逻辑存入你的离线存储器,在遭遇系统崩溃或需要离线检索时,实现瞬时读取。

【💬】 协议加密解密:评论区留下你的散列码。分享你曾遭遇的代码冲突或系统漏洞(那些年踩过的坑),通过交互式编译共同绕过技术陷阱。

【🛰️】 信号频率投票:通过投票发射你的选择。你的每一次点击都在重新定义矩阵的进化方向,决定下一个被全量拆解的技术节点。


在这里插入图片描述

Read more

Flutter 三方库 simple_observable 的鸿蒙化适配指南 - 在鸿蒙系统上构建极致、轻量级的响应式数据观察与状态协同引擎

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 simple_observable 的鸿蒙化适配指南 - 在鸿蒙系统上构建极致、轻量级的响应式数据观察与状态协同引擎 在鸿蒙(OpenHarmony)系统开发中,如何以最少的代码实现组件间的高效通信?当传统的 Provider 或 BLoC 显得过重时,simple_observable 为开发者提供了一套极简的观察者模式(Observer Pattern)实现方案。本文将深入实战其在鸿蒙生态中的轻量级应用。 前言 什么是 Observable(可观察对象)?它描述了一种“数据变化自动通知”的订阅模型。simple_observable 库抛弃了复杂的 Rx 操作符,仅保留了最核心的监听与触发逻辑。在 Flutter for OpenHarmony 的实际开发中,利用该库,我们可以非常方便地在鸿蒙应用的 Service

By Ne0inhk
HarmonyOS:知识点总结(一)

HarmonyOS:知识点总结(一)

一、LazyForEach: 数据懒加载 LazyForEach必须在容器组件内使用,仅有List、Grid、Swiper以及WaterFlow组件支持数据懒加载(可配置cachedCount属性,即只加载可视部分以及其前后少量数据用于缓冲),其他组件仍然是一次性加载所有的数据。支持数据懒加载的父组件根据自身及子组件的高度或宽度计算可视区域内需布局的子节点数量,高度或宽度的缺失会导致部分场景懒加载失效。 List设置cachedCount后,显示区域外上下各会预加载并布局cachedCount行ListItem。计算ListItem行数时,会计算ListItemGroup内部的ListItem行数。如果ListItemGroup内没有ListItem,则整个ListItemGroup算一行。 List下嵌套使用LazyForEach,并且LazyForEach下嵌套使用ListItemGroup时,LazyForEach会在List显示区域外上下各会创建cachedCount个ListItemGroup。 二、ohos.data.preferences (用户首选项) get

By Ne0inhk
Flutter for OpenHarmony:postgrest 直接访问 PostgreSQL 数据库的 RESTful 客户端(Supabase 核心驱动) 深度解析与鸿蒙适配指南

Flutter for OpenHarmony:postgrest 直接访问 PostgreSQL 数据库的 RESTful 客户端(Supabase 核心驱动) 深度解析与鸿蒙适配指南

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 如果你不想写复杂的 Java/Node.js 后端服务,只想直接增删改查数据库,PostgREST 是一个神奇的后端工具 —— 它能把从 PostgreSQL 数据库自动生成一套 RESTful API。而 postgrest (Dart库) 正是这一服务的官方客户端。 这也是 Supabase(Firebase 的开源替代品)的核心组件之一。通过它,你可以在 Flutter App 中像写 SQL 一样流畅地操作远程数据,既安全又高效。 一、概念介绍/原理解析 1.1 基础概念 * Filter: eq('id', 1), gt('

By Ne0inhk

Linux 基础命令大全(入门必备)

# Linux 基础命令大全(入门必备) Linux 命令是操作 Linux 系统的核心,以下整理了最常用的基础命令,涵盖文件操作、目录管理、系统信息、权限控制等核心场景,附带示例和说明,适合新手快速上手。 ## 一、文件与目录操作(最常用) ### 1. 目录切换 `cd` 切换当前工作目录,是使用频率最高的命令。 ```bash cd /home          # 切换到绝对路径 /home(从根目录开始) cd Documents      # 切换到相对路径 Documents(当前目录下的子目录) cd ..             # 切换到上一级目录 cd ~              # 切换到当前用户的家目录(如 root 用户是 /root,普通用户是 /home/用户名) cd -              # 切换到上一次所在的目录 ``` ### 2. 查看目录内容 `ls`

By Ne0inhk