C++内存池实现

1.内存池概念

内存池就和其他的池数据(如线程池)结构类似,由程序维护一个“池”结构来管理程序使用的内存,然后根据需要从内存池中申请使用内存或者向内存池中释放内存,来达到高效管理内存的目的。
在一般的内存管理的库函数中,不管是C中的malloc/free还是C++中的New/delet,都涉及到一个问题:在申请和释放内存时,都需要与操作系统进行交互来完成内存的分配,而在释放时,系统需要对申请的堆内存就行整理,判断free的内存块前后是否有空闲,如果有的话就需要进行合并,因此,直接使用库函数来向系统申请内存会耗费大量的时间,同时也可能产生大量的内存碎片,对系统整体造成压力。
因此,对于需要在短时间内大量申请或者释放小块内存的系统,维护一个内存池来管理内存的分配和回收,在提高系统的效率和并发能力上就很有意义了。
内存池的原理就是,由程序在初始化时一次性向系统申请一块大的内存,然后将其分成多个固定大小的内存块来进行管理,从而避免程序运行时频繁的进行系统调用来减少内存碎片和分配开销。

2.内存池框架

在这里的内存池实现框架中,把内存池分为前端和后端两个部分,由后端维护16个自由链表,在每个链表下挂载管理相同大小的内存块,从8,16,24到128,在申请使用时,当申请的内存小于128字节时,从内存池中分配对应大小的内存块给对象,如果申请的内存大于128字节,则使用malloc从系统中获取。在释放内存时,从内存池申请的内存释放给内存池的管理工具,而从系统申请的内存则释放给系统,由系统进行维护。
内存池维护的数据结构前端使用类模板,通过重新定义一个New类和Delete类来帮助使用者利用内存池管理内存。

3.工具类:Mutex

这里是封装一个锁,来保证在内存池使用时的线程安全。

//Mutex.h//
// Created by crab on 2024/10/28.
//#ifndef MUTEX_H
#define MUTEX_H
#include <pthread.h>class Mutex
{
public://创建锁static Mutex* createNew();//构造与析构函数Mutex();~Mutex();//加锁 解锁void lock();void unlock();//获取锁的对象pthread_mutex_tpthread_mutex_t* get() { return &mMutex; };private:pthread_mutex_t mMutex;};
//Guard对象,用来保证锁与加锁的生命周期一致
class MutexLockGuard
{
public:MutexLockGuard(Mutex* mutex);~MutexLockGuard();private:Mutex* mMutex;};
#endif //MUTEX_H
//Mutex.cpp
//
// Created by crab on 2024/10/28.
//#include "Mutex.h"
#include "New.h"Mutex* Mutex::createNew()
{//return new Mutex();return New<Mutex>::allocate();
}Mutex::Mutex()
{pthread_mutex_init(&mMutex, NULL);
}Mutex::~Mutex()
{pthread_mutex_destroy(&mMutex);
}void Mutex::lock()
{pthread_mutex_lock(&mMutex);
}void Mutex::unlock()
{pthread_mutex_unlock(&mMutex);
}MutexLockGuard::MutexLockGuard(Mutex* mutex) :mMutex(mutex)
{mMutex->lock();
}MutexLockGuard::~MutexLockGuard()
{mMutex->unlock();
}

4.代码概览

4.1内存池后端

//Allocator.h
//
// Created by crab on 2024/10/28.
//#ifndef ALLOCATOR_H
#define ALLOCATOR_H#include <cstdint>
#include <cstdlib>
#include <cstring>#include "Mutex.h"//内存池
//单例类,通过getInstance获取唯一实例
class Allocator
{
public:enum {ALIGN = 8};enum {MAX_BYTES = 128};enum {NFREELISTS = MAX_BYTES / ALIGN};union Obj {union Obj* next;char data[1];};static void* allocate(uint32_t size);static void deallocate(void* p, uint32_t size);private:Allocator() : mStartFree(NULL), mEndFree(NULL), mHeapSize(0){mMutex = new Mutex;memset(mFreeList, 0, sizeof(mFreeList));};~Allocator() {};static Allocator* getInstance();void* alloc(uint32_t size);void dealloc(void* p, uint32_t size);/* 获取指定字节数在自由链表的下标 */uint32_t freelistIndex(uint32_t bytes) {return (((bytes) + ALIGN-1) / ALIGN - 1);}/* 字节对齐 */uint32_t roundup(uint32_t bytes) {return (((bytes) + ALIGN-1) & ~(ALIGN - 1));}void *refill(uint32_t bytes);char* chunkAlloc(uint32_t size, int& nobjs);private:static Allocator* mAllocator;Mutex* mMutex;/* 维护缓存块 */char* mStartFree;char* mEndFree;uint32_t mHeapSize;Obj* mFreeList[NFREELISTS];};
#endif //ALLOCATOR_H
//Allocator.cpp
//
// Created by crab on 2024/10/28.
//#include "Allocator.h"
#include <cstdlib>
#include <iostream>Allocator* Allocator::mAllocator = NULL;void* Allocator::allocate(uint32_t size)
{return getInstance()->alloc(size);
}void Allocator::deallocate(void* p, uint32_t size)
{getInstance()->dealloc(p, size);
}Allocator* Allocator::getInstance()
{if(!mAllocator)mAllocator = new Allocator();return mAllocator;
}void* Allocator::alloc(uint32_t size)
{Obj* result;uint32_t index;MutexLockGuard mutexLockGuard(mMutex);/* 如果分配内存大于 MAX_BYTES,那么就直接通过 malloc 分配 */if(size > MAX_BYTES)return malloc(size);index = freelistIndex(size);result = mFreeList[index];/* 如果没有找到则重新分配内存 */if(!result){void* r = refill(roundup(size));return r;}/* 找到了就从链表中删除内存块 */mFreeList[index] = result->next;return result;
}void Allocator::dealloc(void* p, uint32_t size)
{Obj* obj = (Obj*)p;uint32_t index;MutexLockGuard mutexLockGuard(mMutex);/* 如果释放内存大于 MAX_BYTES,那么就直接通过 free 释放 */if(size > MAX_BYTES)free(p);index = freelistIndex(size); //获取该大小在freelist的下标/* 将内存块添加进链表中 */obj->next = mFreeList[index];mFreeList[index] = obj;
}/* 重新分配内存 */
void* Allocator::refill(uint32_t bytes)
{int nobjs = 20;char* chunk = chunkAlloc(bytes, nobjs); //分配内存Obj* result;Obj* currentObj;Obj* nextObj;int i;uint32_t index;/* 如果只有一个节点,那么直接放回,不需要处理剩余内存 */if(1 == nobjs)return chunk;result = (Obj*)chunk;index = freelistIndex(bytes);mFreeList[index] = nextObj = (Obj*)(chunk + bytes);/* 将剩余内存连成链表 */for(i = 1; ; ++i){currentObj = nextObj;nextObj = (Obj*)((char*)nextObj + bytes);if(nobjs-1 == i) //最后一个节点{currentObj->next = 0;break;}else{currentObj->next = nextObj;}}return result;
}char* Allocator::chunkAlloc(uint32_t size, int& nobjs)
{char* result;uint32_t totalBytes = size * nobjs; //总共需求的内存uint32_t bytesLeft = mEndFree - mStartFree; //缓存块中剩余的内存大小if(bytesLeft > totalBytes) //如果缓存块的内存满足需求,则直接从缓存块中获取内存{result = mStartFree;mStartFree += totalBytes;return result;}else if(bytesLeft > size) //如果缓存块剩余大小大于一个节点的大小,则尽可能返回多个节点{nobjs = bytesLeft / size;totalBytes = size * nobjs;result = mStartFree;mStartFree += totalBytes;return result;}else{uint32_t bytesToGet = 2 * totalBytes + roundup(mHeapSize >> 4); //至少两倍增长if(bytesLeft > 0) //如果缓存块还剩余内存,那么它肯定可以插入到某个节点中{uint32_t index = freelistIndex(bytesLeft);((Obj*)(mStartFree))->next = mFreeList[index];mFreeList[index] = (Obj*)mStartFree;}/* 重新申请内存 */mStartFree = (char*)malloc(bytesToGet);mHeapSize += bytesToGet;mEndFree = mStartFree + bytesToGet;/* 递归调用chunkAlloc,重新分配 */return chunkAlloc(size, nobjs);}
}

4.2内存池前端

//Construct.h
//
// Created by crab on 2024/10/28.
//#ifndef CONSTRUCT_H
#define CONSTRUCT_H#include <new>//在特定内存位置上构造或销毁对象,与内存池连用template <class T>
inline void destroy(T* pointer)
{pointer->~T();
}template <class T>
inline void construct(T* p)
{new (p) T();
}template <class T, class T1>
inline void construct(T* p, const T1& a1)
{new (p) T(a1);
}template <class T, class T1, class T2>
inline void construct(T* p, const T1& a1, const T2& a2)
{new (p) T(a1, a2);
}template <class T, class T1, class T2, class T3>
inline void construct(T* p, const T1& a1, const T2& a2, const T3& a3)
{new (p) T(a1, a2, a3);
}template <class T, class T1, class T2, class T3, class T4>
inline void construct(T* p, const T1& a1, const T2& a2, const T3& a3, const T4& a4)
{new (p) T(a1, a2, a3, a4);
}#endif //CONSTRUCT_H
//New.h
//
// Created by crab on 2024/10/28.
//#ifndef NEW_H
#define NEW_H#include "Allocator.h"
#include "Construct.h"#define     ALLOCATOR       Allocatortemplate <class T>
class New
{
public:typedef     T           Value;typedef     T*          Point;typedef     T&          Ref;typedef     ALLOCATOR   Alloc;public:static Point allocate() {Point obj = (Point)Alloc::allocate(sizeof(Value));construct(obj);return obj;}template <class T1>static Point allocate(const T1& a1) {Point obj = (Point)Alloc::allocate(sizeof(Value));construct(obj, a1);return obj;}template <class T1, class T2>static Point allocate(const T1& a1, const T2& a2) {Point obj = (Point)Alloc::allocate(sizeof(Value));construct(obj, a1, a2);return obj;}template <class T1, class T2, class T3>static Point allocate(const T1& a1, const T2& a2, const T3& a3) {Point obj = (Point)Alloc::allocate(sizeof(Value));construct(obj, a1, a2, a3);return obj;}template <class T1, class T2, class T3, class T4>static Point allocate(const T1& a1, const T2& a2, const T3& a3, const T4& a4) {Point obj = (Point)Alloc::allocate(sizeof(Value));construct(obj, a1, a2, a3, a4);return obj;}
};class Delete
{
public:typedef     ALLOCATOR   Alloc;template <class T1>static void release(T1* point) {destroy(point);Alloc::deallocate(point, sizeof(T1));}};#endif //NEW_H

5.代码解释

1.内存池后端

//
// Created by crab on 2024/10/28.
//#ifndef ALLOCATOR_H
#define ALLOCATOR_H#include <cstdint>
#include <cstdlib>
#include <cstring>#include "Mutex.h"//内存池
//单例类,通过getInstance获取唯一实例
class Allocator
{
public://ALIGN:内存块的对齐单位常量enum {ALIGN = 8};//Max_Bytes:内存块的最大字节数enum {MAX_BYTES = 128};//NFREELISTS:自由链表数量enum {NFREELISTS = MAX_BYTES / ALIGN};//一个union联合体,用来作为内存块节点的数据结构。 union Obj {union Obj* next;char data[1];};//根据请求的size的分配内存static void* allocate(uint32_t size);//释放指定大小的内存块pstatic void deallocate(void* p, uint32_t size);private://构造函数,初始化内存池的起始和结束指针mStartFree和mEndFree,堆大小mHeapSize,分配一个Mutex对象管理线程安全Allocator() : mStartFree(NULL), mEndFree(NULL), mHeapSize(0){mMutex = new Mutex;memset(mFreeList, 0, sizeof(mFreeList));};~Allocator() {};//静态方法用来获取Allocator的唯一实例static Allocator* getInstance();//内存分配void* alloc(uint32_t size);//内存释放void dealloc(void* p, uint32_t size);/* 获取指定字节数在自由链表的下标 *///快速找到该大小的链表头,如:16字节的内存:(16 + 8 - 1)/ 8 - 1= 1 uint32_t freelistIndex(uint32_t bytes) {return (((bytes) + ALIGN-1) / ALIGN - 1);}/* 字节对齐 *///将给定的字节数向上取整为8的倍数,实现快速对齐 //eg: 14字节: (14+8-1)~(8-1)=16 : ~(8-1) 7的二进制取反:11111000, 然后将21与11111000进行与运算,结果为16uint32_t roundup(uint32_t bytes) {return (((bytes) + ALIGN-1) & ~(ALIGN - 1));}//补充指定大小的内存块void *refill(uint32_t bytes);//从堆上分配多个内存块char* chunkAlloc(uint32_t size, int& nobjs);private://Allocator的静态实例static Allocator* mAllocator;//锁Mutex* mMutex;/* 维护缓存块 */char* mStartFree;char* mEndFree;uint32_t mHeapSize;//指针数组,用来维护内存块链表Obj* mFreeList[NFREELISTS];};
#endif //ALLOCATOR_H
//
// Created by crab on 2024/10/28.
//#include "Allocator.h"
#include <cstdlib>
#include <iostream>Allocator* Allocator::mAllocator = NULL;void* Allocator::allocate(uint32_t size)
{//通过Allocator的Instance调用alloc分配size大小的内存块return getInstance()->alloc(size);
}void Allocator::deallocate(void* p, uint32_t size)
{//delloc释放getInstance()->dealloc(p, size);
}Allocator* Allocator::getInstance()
{if(!mAllocator)mAllocator = new Allocator();return mAllocator;
}//分配内存,如果需要的内存大于MAX_BYTES,直接用malloc分配
void* Allocator::alloc(uint32_t size)
{Obj* result;uint32_t index;//加锁,确保线程安全MutexLockGuard mutexLockGuard(mMutex);/* 如果分配内存大于 MAX_BYTES,那么就直接通过 malloc 分配 */if(size > MAX_BYTES)return malloc(size);//获取内存块在链表数组中的位置,然后从mFreeList中获取对应链表的上的内存块index = freelistIndex(size);result = mFreeList[index];/* 如果没有找到则重新分配内存 */if(!result){void* r = refill(roundup(size));return r;}/* 找到了就从链表中删除内存块 */mFreeList[index] = result->next;return result;
}void Allocator::dealloc(void* p, uint32_t size)
{Obj* obj = (Obj*)p;uint32_t index;MutexLockGuard mutexLockGuard(mMutex);/* 如果释放内存大于 MAX_BYTES,那么就直接通过 free 释放 */if(size > MAX_BYTES)free(p);index = freelistIndex(size); //获取该大小在freelist的下标/* 将内存块添加进链表中 */obj->next = mFreeList[index];mFreeList[index] = obj;
}/* 重新分配内存 */
void* Allocator::refill(uint32_t bytes)
{int nobjs = 20;char* chunk = chunkAlloc(bytes, nobjs); //分配内存Obj* result;Obj* currentObj;Obj* nextObj;int i;uint32_t index;/* 如果只有一个节点,那么直接放回,不需要处理剩余内存 */if(1 == nobjs)return chunk;result = (Obj*)chunk;index = freelistIndex(bytes);mFreeList[index] = nextObj = (Obj*)(chunk + bytes);/* 将剩余内存连成链表 */for(i = 1; ; ++i){currentObj = nextObj;nextObj = (Obj*)((char*)nextObj + bytes);if(nobjs-1 == i) //最后一个节点{currentObj->next = 0;break;}else{currentObj->next = nextObj;}}return result;
}char* Allocator::chunkAlloc(uint32_t size, int& nobjs)
{char* result;uint32_t totalBytes = size * nobjs; //总共需求的内存uint32_t bytesLeft = mEndFree - mStartFree; //缓存块中剩余的内存大小if(bytesLeft > totalBytes) //如果缓存块的内存满足需求,则直接从缓存块中获取内存{result = mStartFree;mStartFree += totalBytes;return result;}else if(bytesLeft > size) //如果缓存块剩余大小大于一个节点的大小,则尽可能返回多个节点{nobjs = bytesLeft / size;totalBytes = size * nobjs;result = mStartFree;mStartFree += totalBytes;return result;}else{uint32_t bytesToGet = 2 * totalBytes + roundup(mHeapSize >> 4); //至少两倍增长if(bytesLeft > 0) //如果缓存块还剩余内存,那么它肯定可以插入到某个节点中{uint32_t index = freelistIndex(bytesLeft);((Obj*)(mStartFree))->next = mFreeList[index];mFreeList[index] = (Obj*)mStartFree;}/* 重新申请内存 */mStartFree = (char*)malloc(bytesToGet);mHeapSize += bytesToGet;mEndFree = mStartFree + bytesToGet;/* 递归调用chunkAlloc,重新分配 */return chunkAlloc(size, nobjs);}
}
//Construct.h
// Created by crab on 2024/10/28.
//#ifndef CONSTRUCT_H
#define CONSTRUCT_H#include <new>//在特定内存位置上构造或销毁对象,与内存池连用template <class T>
inline void destroy(T* pointer)
{pointer->~T();
}template <class T>
inline void construct(T* p)
{new (p) T();
}template <class T, class T1>
inline void construct(T* p, const T1& a1)
{new (p) T(a1);
}template <class T, class T1, class T2>
inline void construct(T* p, const T1& a1, const T2& a2)
{new (p) T(a1, a2);
}template <class T, class T1, class T2, class T3>
inline void construct(T* p, const T1& a1, const T2& a2, const T3& a3)
{new (p) T(a1, a2, a3);
}template <class T, class T1, class T2, class T3, class T4>
inline void construct(T* p, const T1& a1, const T2& a2, const T3& a3, const T4& a4)
{new (p) T(a1, a2, a3, a4);
}#endif //CONSTRUCT_H
//New.h
// Created by crab on 2024/10/28.
//#ifndef NEW_H
#define NEW_H#include "Allocator.h"
#include "Construct.h"#define     ALLOCATOR       Allocatortemplate <class T>
class New
{
public:typedef     T           Value;typedef     T*          Point;typedef     T&          Ref;typedef     ALLOCATOR   Alloc;public:static Point allocate() {Point obj = (Point)Alloc::allocate(sizeof(Value));construct(obj);return obj;}template <class T1>static Point allocate(const T1& a1) {Point obj = (Point)Alloc::allocate(sizeof(Value));construct(obj, a1);return obj;}template <class T1, class T2>static Point allocate(const T1& a1, const T2& a2) {Point obj = (Point)Alloc::allocate(sizeof(Value));construct(obj, a1, a2);return obj;}template <class T1, class T2, class T3>static Point allocate(const T1& a1, const T2& a2, const T3& a3) {Point obj = (Point)Alloc::allocate(sizeof(Value));construct(obj, a1, a2, a3);return obj;}template <class T1, class T2, class T3, class T4>static Point allocate(const T1& a1, const T2& a2, const T3& a3, const T4& a4) {Point obj = (Point)Alloc::allocate(sizeof(Value));construct(obj, a1, a2, a3, a4);return obj;}
};class Delete
{
public:typedef     ALLOCATOR   Alloc;template <class T1>static void release(T1* point) {destroy(point);Alloc::deallocate(point, sizeof(T1));}};#endif //NEW_H

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构-二叉树

一.二叉树的定义 二叉树有左右儿子之分 完美二叉树&#xff08;满二叉树&#xff09;除了最下面的没有儿子其他结点都有两个儿子&#xff0c;叶节点比较齐的&#xff0c;完全二叉树不是满二叉数允许缺失最后的结点 满二叉树可以达到2^k-1 边的总数节点数-1 二.二叉树的存储结构…

OKR制定指南

Goal Crafting 目标制定是最基本的领导活动之一。组织绩效和团队成长依赖于精心制定的目标。没有良好的目标制定练习&#xff0c;团队可能只关注眼前的事务&#xff0c;解决看似可以快速解决的问题。良好的目标制定迫使你不忽视或推迟那些需要新思维方式、合作或克服困难的问题…

详细分析Java中FilterChain过滤器的基本知识

目录 前言1. 基本知识2. Demo 前言 基本的Java知识推荐阅读&#xff1a; java框架 零基础从入门到精通的学习路线 附开源项目面经等&#xff08;超全&#xff09;【Java项目】实战CRUD的功能整理&#xff08;持续更新&#xff09; 从实战中学习&#xff1a; 常用在一些重复代…

TableGPT2-7B:用于表格数据分析的大规模解码器模型

TableGPT2-7B 是浙江大学开发的最先进的大规模解码器模型&#xff0c;专为涉及表格数据的数据密集型任务而设计。该模型以 Qwen2.5 架构为基础&#xff0c;包括针对表格数据的专用编码&#xff0c;其中独特的语义编码器可从行、列和整个表格中获取洞察力。 主要特点和功能 Ta…

SQL面试题——抖音SQL面试题 主播播出时长

主播播出时长 现有如下数据,主播id、房间号、播出的批次号,每个批次号进出房间的时间戳、分区时间: 每一次直播都有一个上播和下播,每个房间里,同一个批次号会有两条数据,分别记录了上播和下播时间,求每个主播的播出时长? 通过上面的数据,可以清晰的看出,同一个批次…

数字信号处理Python示例(14)生成锯齿波和三角波

文章目录 前言一、锯齿波和三角波二、生成锯齿波和三角波的Python代码三、仿真结果及分析写在后面的话 前言 因其独特的数学特性和物理表现&#xff0c;在工程和技术领域扮演着重要角色。这是生成非正弦信号的几个Python示例的其中一个&#xff0c;生成三角波与锯齿波&#xf…

HBase理论_HBase架构组件介绍

近来有些空闲时间&#xff0c;正好最近也在开发HBase相关内容&#xff0c;借此整理一下学习和对HBase组件的架构的记录和个人感受&#xff0c;付出了老夫不少心血啊&#xff0c;主要介绍的就是HBase的架构设计以及我的拓展内容。内容如有不当或有其他理解 matirx70163.com HB…

前端快速上手(一):HTML

目录 1. HTML 基础 1.1 HTML 标签 1.2 标签的结构关系 2. HTML 常见标签 2.1 标题标签: h1 - h6 2.2 段落标签: p 2.3 换行标签: br 2.4 图片标签: img 2.5 超链接: a 标签 2.5.1 外部链接 2.5.2 内部链接 2.5.3 文件资源链接 2.5.4 空链接 2.6 表格标签 2.7 表单…

QT<30> Qt中使鼠标变为转圈忙状态

前言&#xff1a;当我们在写软件时&#xff0c;在等待阻塞耗时操作时可以将鼠标变为忙状态&#xff0c;并在一段时间后恢复状态&#xff0c;可以用到GxtWaitCursor&#xff1a;Qt下基于RAII的鼠标等待光标类。 一、效果演示 二、详细代码 在项目中添加C文件&#xff0c;命名为…

Shell环境导致编译失败处理方法

在嵌入式Linux系统源码&#xff08;BSP包&#xff09;编译时&#xff0c;有可能会如现如下提示&#xff1a; [[: not found 这种提示&#xff0c;一般是Shell环境为dash而不是bash导致&#xff0c;可以通过如下命令来切换&#xff1a; sudo dpkg-reconfigure dash 执行后会…

nginx openresty lua-resty-http 使用的一些问题记录

需求背景 需求是使用 nginx 做一个 https 服务的代理 nginx 收到 http 请求后&#xff0c;需要修改 body 中的某些参数值&#xff0c;然后将修改后的数据发送到目标服务器&#xff08;https&#xff09; 本来以为很简单的需求&#xff0c;结果中间出现了不少岔子&#xff0c;这…

Redis的分布式锁分析

系列文章目录 Java项目对接redis&#xff0c;客户端是选Redisson、Lettuce还是Jedis&#xff1f; 由Redis引发的分布式锁探讨 系列文章目录一、什么是分布式锁&#xff1f;二、Redis分布式锁的几种实现1. 简单分布式锁2. Redlock 三、Redis 锁的问题1. 互斥失效2. 时钟偏移 四…

柯桥生活英语口语学习“面坨了”英语怎么表达?

“面坨了”英语怎么表达&#xff1f; 要想搞清楚这个表达&#xff0c;首先&#xff0c;我们要搞明白“坨”是啥意思&#xff1f; 所谓“坨”就是指&#xff0c;面条在汤里泡太久&#xff0c;从而变涨&#xff0c;黏糊凝固在一起的状态。 有一个词汇&#xff0c;很适合用来表达这…

鸿蒙NEXT应用示例:切换图片动画

【引言】 在鸿蒙NEXT应用开发中&#xff0c;实现图片切换动画是一项常见的需求。本文将介绍如何使用鸿蒙应用框架中的组件和动画功能&#xff0c;实现不同类型的图片切换动画效果。 【环境准备】 电脑系统&#xff1a;windows 10 开发工具&#xff1a;DevEco Studio NEXT B…

UAC2.0 speaker——speaker 数据传输

文章目录 麦克风数据传输准备音频数据抓包原始数据频谱分析(FFT)应用麦克风数据传输 上一节中实现了 USB 麦克风设备 本节主要介绍 MCU 麦克风的数据如何传输给上位机。 准备音频数据 MCU 端发送 48KHZ, 16bit 单声道的正弦波数据,正弦波数据的生成参考 音频——C语言生…

【多语言】每种语言打印helloworld,编译为exe会占多大空间

文章目录 背景c语言 53KBc 53KBgo 1.8Mdart 4.6Mpython未测试nodejs未测试rust未测试java未测试cmd || bash || powershell 未测试other 背景 各个版本的helloworld&#xff0c;纯属闲的, 环境如下: - win10 - mingw: gcc8.1.0 - go1.21 - dart3.5.4c语言 53KB gcc main.c -…

Android12的ANR解析

0. 参考&#xff1a; ANR分析 深入理解 Android ANR 触发原理以及信息收集过程 1.ANR的触发分类: ANR分为4类&#xff1a; InputDispatchTimeout&#xff1a;输入事件分发超时5s,包括按键和触摸事件。BroadcastTimeout&#xff1a;比如前台广播在10s内未执行完成&#xff0…

2022-2023全国高校计算机能力挑战赛区域赛python组编程题

mi目录 2022 1. 2. 1. 使用 format() 方法 2. 使用 f-string&#xff08;Python 3.6 及以上&#xff09; 2023 1. 2. 3. 4 闽农大宝玲楼 2022 1. 1.某动物研究员给动物园的动物们定了一个园区幸福值&#xff0c;其中园区幸福值的计算为一个园区内“所有动物的活动时…

函数的栈帧

前言&#xff1a; 1.请使用vs2013调试&#xff0c;我使用vs2019被恶心到了&#xff0c;封装严重&#xff0c;不利于观察。 2.函数栈帧&#xff1a;函数就是程序&#xff0c;程序就需要空间来运行&#xff0c;所以我们要为他分配空间&#xff0c;分配的空间用ebp esp维护&…

机器学习基础04

目录 1.朴素贝叶斯-分类 1.1贝叶斯分类理论 1.2条件概率 1.3全概率公式 1.4贝叶斯推断 1.5朴素贝叶斯推断 1.6拉普拉斯平滑系数 1.7API 2.决策树-分类 2.1决策树 2.2基于信息增益的决策树建立 2.2.1信息熵 2.2.2信息增益 2.2.3信息增益决策树建立步骤 2.3基于基…