- 博客(124)
- 资源 (1)
- 收藏
- 关注

原创 Linux内存系统简介
功能:动态分配 / 释放用户空间内存(堆内存)。底层实现:小内存(通常 < 128KB):通过brk调整堆的边界(),分配连续空间,减少系统调用开销;大内存(通常 ≥ 128KB):通过mmap映射匿名内存(),避免堆碎片累积;malloc内部维护内存池(如ptmalloc的arena结构),减少频繁分配 / 释放的开销,支持线程安全(通过互斥锁)。free不会立即归还内存给内核,而是标记为空闲,供后续malloc复用(减少系统调用);若内存长期未使用,大内存块会通过munmap。
2025-07-17 23:05:57
981
原创 C++原子操作之手写无锁队列
在并发编程中,原子性、可见性和有序性是确保程序正确执行的三大特性。常见的保证这三个特性的操作是通过加锁来限制资源的访问,但这种方式会带来性能的降低,所以无锁编程变的日益常见。本文将对原子性、可见性和有序性进行介绍,同时介绍原子操作和内存顺序从而实现无锁的并发。最后会给出一个无锁队列的完备例子无锁队列的实现单元测试功能测试性能测试压力测试。
2025-07-30 09:34:24
788
原创 从实践出发--探究C/C++空类的大小,真的是1吗?
在VC环境下,从VS2017开始C语言是不允许定义空类的,因此会报错C++允许空类,大小为1在Gcc环境下C语言可以定义空类,大小为0C++可以定义空类,大小为1。
2025-07-21 23:04:53
394
原创 手写std::optional:告别空指针的痛苦
std::optional是一个模板类,它可以包含一个值,也可以不包含值(即为空)。这听起来很简单,但它解决了C++中一个长期存在的问题:如何优雅地表示"可能没有值"的情况。在传统C++中,我们通常使用以下方式来表示可选值:● 使用特殊值(如-1、nullptr)● 使用指针和nullptr● 使用布尔标志配合实际值● 抛出异常这些方法都有各自的问题:特殊值可能与正常值冲突,指针容易导致内存泄漏,布尔标志增加了代码复杂性,异常处理成本高昂。总结。
2025-07-13 23:19:49
901
原创 手写渐进式Hash
该哈希表使用双表结构实现渐进式 rehash,避免在哈希表扩容时一次性进行大量数据迁移导致的性能开销。对外提供的哈希表接口,使用双表实现渐进式 rehash。对外提供的哈希表接口,使用双表实现渐进式 rehash。单张哈希表,包含槽位数组。(用于计算索引)和键数量。
2025-07-12 11:24:34
361
原创 C++迭代器失效
迭代器:是C++中算法和容器之间的桥梁,主要目的是为了提供一种类指针的工具来操作容器。迭代器、指针、引用,在STL中本质是指向容器元素的句柄。// it就是一个迭代器,指向vector的第一个元素// 输出1元素被删除/插入容器进行扩容或者重排hash表的rehash底层数据的迁移这时候,句柄还可能被用户拿着,指向修改之前的内容,而继续操作这些原有的句柄,就会导致UB行为,最可能的情况就是段错误。| 操作 | 容器 | 策略 |
2025-07-11 23:45:50
410
原创 【IO复用】五种IO模型
进程发起IO系统调用后,如果内核缓冲区没有数据,需要到IO设备中读取,进程返回一个错误而不会被阻塞;在linux系统中,实际上所有的I/O设备都被抽象为了文件这个概念,一切皆文件,磁盘、网络数据、终端,甚至进程间通信工具管道pipe等都被当做文件对待。实现难度低,适用并发量小的网络应用开发,不适用并发量大的应用,因为一个请求IO会阻塞进程,所以每请求分配一个处理进程(线程)去响应,系统开销大。也就是说,数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓存区拷贝到应用程序的地址空间中。
2025-07-10 20:18:36
515
原创 手写STL之vector
是表示可以改变大小的数组的序列容器,底层是线性表数组。可以使用指向其元素的常规指针上的偏移量来访问它们的元素,并且与在数组中一样高效。但是与C语言的数组不同,它们的大小可以动态变化,容器会自动处理它们的存储。在内部,vector 使用一个动态分配的数组来存储它们的元素。这个数组可能需要重新分配,以便在插入新元素时增大大小,这意味着分配一个新数组并将所有元素移动到其中。就处理时间而言,这是一项相对昂贵的任务,因此,vector 不会在每次向容器添加元素时重新分配。
2025-07-09 20:37:33
357
原创 C++异步编程入门
在将promise封装为packaged_task后,我们依然有大量的需求是创建一个线程,去执行异步操作,在计算机的世界里面,没有什么是再封一层做不到的,如果有,那一定就是再封两层,所以我们将packaged_task和thread再向上封装,就形成了最后的async接口,这个接口直接返回future,然后在**某个时间点**完成异步操作。假设我们将p传入到某一个线程,在这个线程内部,调用p的set_value接口,在另一个线程内部,调用f的get接口,就可以实现线程间的异步同步处理。
2025-07-08 23:21:13
655
原创 哈希表索引计算的优化方案
在二进制表示中,2n 是一个1后面跟着 n 个0的数,例如 24=16 的二进制表示是。这是因为 2n 的倍数在二进制表示中,低 n 位都是0。的二进制表示是 n 个1,例如 24−1=15 的二进制表示是。当哈希表的大小是2的幂次时,可以使用位运算来替代取模运算。的低 n 位保持不变,高 n 位被清零。当哈希表的大小是 2n 时,我们需要将哈希值。的高 n 位清零,保留低 n 位。可以看到,两种方法的结果是一样的,都是。的结果相同,因为取模运算也是保留。的高 n 位清零,保留低 n 位。
2025-06-16 11:40:30
198
原创 RTMP协议解析【三】
本专栏重点负责介绍RTMP协议的理论部分, 跳过定义,协议与其他协议的优缺点对比,协议的拓展与改进,协议的历史发展等其他废话。窗口大小是指发送者在没有收到接收者的确认消息之前发送的最大字节数,本控制消息定义了序列号也就是目前为止收到的字节数。本控制消息用来在客户端或者服务端接收到等同于窗口大小的字节后必须发送对端一个确认消息。用户控制消息用于告知对方执行该信息中包含的用户控制时间,消息类型为4,并且。本控制消息用于通知对端,如果正在等待一条消息的部分块(一个。占4个字节,表示确认窗口的大小,单位为字节。
2025-05-21 23:21:43
285
原创 RTMP协议解析【二】
本专栏重点负责介绍RTMP协议的理论部分, 跳过定义,协议与其他协议的优缺点对比,协议的拓展与改进,协议的历史发展等其他废话。由于篇幅原因,本文先简介RTMP Chunk,关注我,后续继续详细介绍RTMP协议。正如TCP握手一般,在RTMP握手后,就可以传输数据,在。描述的是实际传递的消息的头部,而该字段的格式和长度取决于。第一个字节的后6位取值为9,因此,后面1字节的。存储在接下来的字节中,本字段则为标识字段。结构和取值0类似,第一个字节的后6。,表示[0, 255]的取值范围。
2025-05-21 22:49:33
1076
原创 RTMP协议解析[一]
offset字段: 4 bytes,digest的位置信息,offset=offset[0]+offset[1]+offset[2]+offset[3] random-data: (offset) bytes,随机数据。digest的计算方法:digest-data左边部分+digest-data右边部分的内容,以固定的key,做hmac-sha256计算得到digest。本专栏重点负责介绍RTMP协议的理论部分, 跳过定义,协议与其他协议的优缺点对比,协议的拓展与改进,协议的历史发展等其他废话。
2025-05-21 14:28:45
1023
原创 【c++】半同步半异步线程池
项目地址:欢迎pr, st和issue在实际的业务开发场景中, 常常会遇到大量的并发任务, 如果按照传统的处理方式, 一个任务交由一个线程来处理 那么当大量的任务到来时, 此时需要大量的创建线程并且还要处理销毁, 同时还有线程切换的开销, 而池类思想就是预先创建线程, 然后再使用时再进行复用, 减少创建销毁的开销, 只需要等待下次任务的到来即可。这样通过线程池就能避免大量的线程创建和销毁, 从而节省系统资源。
2025-02-06 10:46:53
754
原创 【重学c++primer】第四章第三节 深入浅出:从数组到指针
上述的第二个例子企图使用数组会隐式转换成指针来完成数组的声明,这样在source.cpp文件中,即使更改了数组的下标,在main.cpp中也不需要更改,可以小偷懒。然后,由于在main.cpp文件里,a被视为一个指针,在64位操作系统下,指针占8个字节,因此读取出来的数组为:01 00 00 00 02 00 00 00。比如上面的例子, a的类型是int[3], 但是b的类型是int*, 其中丢失的信息就是3, 3的含义就是元素的个数为3。
2024-08-18 14:14:39
1118
原创 【剑指offer】骰子的点数
将一个骰子投掷 n次,获得的总点数为 s,s 的可能范围为 n∼6n掷出某一点数,可能有多种掷法,例如投掷 2 次,掷出 3点,共有 [1,2],[2,1]两种掷法。请求出投掷 n次,掷出 n∼6n点分别有多少种掷法。数据范围:1≤n≤10样例1输入:n=1输出:[1, 1, 1, 1, 1, 1]解释:投掷1次,可能出现的点数为1-6,共计6种。每种点数都只有1种掷法。所以输出[1, 1, 1, 1, 1, 1]。样例2输入:n=2。
2024-08-03 14:39:30
305
原创 剑指offer题解合集——Week7day1[滑动窗口的最大值]
给定一个数组和滑动窗口的大小,请找出所有滑动窗口里的最大值。例如,如果输入数组 [2,3,4,2,6,2,5,1]及滑动窗口的大小 3,那么一共存在 6个滑动窗口,它们的最大值分别为 [4,4,6,6,6,5]注意:数据保证 k大于 0,且 k小于等于数组长度。数据范围数组长度 [1,1000]样例输入:[2, 3, 4, 2, 6, 2, 5, 1] , k=3输出: [4, 4, 6, 6, 6, 5]
2024-08-01 22:49:32
583
2
原创 【组件协作】 观察者模式
观察者模式是一种组件间协作的设置模式, 用于一对多的通知机制目标发送通知的时候, 无需指定观察者,通知(可以携带通知信息作为参数)会自动传播(调用DoProgress函数)观察者自己确定是否需要订阅通知(订阅通知就是上面的add),目标对象对此一无所知观察者模式常常用于基于事件的UI框架中, 也是MVC模式的一个重要组成成分特别的, 调用DoProgress函数并不是通知观察者, 而是执行通知机制, 只是说执行结果为:通知所有的观察者。
2024-07-31 20:37:55
343
原创 【组件协作】模板方法
定义一个操作中的算法的骨架(稳定, 上面demo的Run方法), 而将一些步骤延迟(变化)到子类中。Template Method使得子类可以不改变(复用)一个算法的结构即可实现重定义(override重写)该算法的某些步骤那么回过头来思考一下, 为什么要把程序的主体流程放在lib库里完成?答案呼之欲出, 因为要稳定但是这样也有弊端:用方法一:业务开发人员不得不完成主流程, 其业务水平会得到很大的提升, 因为你不完成, 整个Application就无法实现。
2024-07-25 22:45:16
546
原创 面向对象设计原则
Shape的抽象, 并没有依赖Line和Rect的细节, 仅有一个Draw接口, 如果此时Shape依赖于Line。Line和Rect属于底层, 是变化的, 而此时MainForm依赖了低层, 违反了DIP原则。Shape是抽象, 是稳定的, 而Line和Rect是细节, 是变化的, 符合DIP原则。如果你的子类不能替代基类, 那你可能需要组合关系, 而不是继承。修改的代价: 修改源代码, 重新编译, 重新测试。动物和生物, 动物继承自生物, 人类继承自动物。汽车继承自交通工具, 摩托车继承自汽车。
2024-07-24 23:19:48
276
原创 【设计模式】总述
说法:“每一个模式描述了在我们周围不断重复发生的问题以及该问题的解决方案的核心”推荐书籍:《设计模式:可复用面向对象软件的基础》《面向对象分析和设计》目标:可复用手法:面向对象当然架构领域也有设计模式,比如:MVC、MVP、MVVM数据库领域也有自己的设计模式但是本设计模式主要是面向对象设计模式需要把握面向对象带来的抽象意义 理解如何利用这些机制来表达现实世界软件设计复杂的根本原因: 变化对于分解:分而治之。
2024-07-23 23:34:30
990
原创 力扣518零钱兑换
一些硬币出来组成amount面额的金钱=>经典的动态规划问题。由此可以看出是:有选择的组合问题=>背包问题。又因为是无限个数=>完全背包问题。
2024-03-25 19:55:07
289
原创 力扣131分隔回文串
给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是。输出:[[“a”,“a”,“b”],[“aa”,“b”]]原先每个元素都要判断一遍是或者不是,需要O(n^2)那么要使得f[i][j]是回文要有两个条件。其实就是预处理了u ~ i是否是回文串。f[i][j]:表示i ~ j都是回文。返回 s 所有可能的分割方案。输入:s = “aab”输入:s = “a”输出:[[“a”]]
2024-03-15 21:08:59
288
1
原创 力扣654 最大二叉树
给定一个不重复的整数数组 nums。输出:[6,3,5,null,2,0,null,null,1]递归地在最大值 左边 的 子数组前缀上 构建左子树。递归地在最大值 右边 的 子数组后缀上 构建右子树。创建一个根节点,其值为 nums 中的最大值。输入:nums = [3,2,1,6,0,5]返回 nums 构建的 最大二叉树。拜完亲戚以后,你加入族谱。我比你大,你做我右儿子。我比你小,我做你左儿子。
2024-03-14 21:24:22
349
原创 【力扣347前k个高频元素】
给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。输入: nums = [1,1,1,2,2,3], k = 2。输入: nums = [1], k = 1。
2024-03-12 21:31:14
249
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人