- 博客(64)
- 收藏
- 关注
原创 从C到C++语法过度1
本文介绍了C++相较于C语言在语法层面的主要改进:1)引入原生字符串类型string,简化了字符串操作;2)新增引用类型作为变量别名;3)提供更安全的新式类型转换(static_cast、const_cast等);4)赋予auto关键字自动类型推导功能。这些特性使C++代码更简洁、安全和高效,尤其体现在字符串处理、类型转换和模板编程中。文章通过代码示例详细对比了新旧语法差异,展示了C++对C语言的扩展与优化。
2025-06-08 13:51:01
1042
原创 C++概念以及基础框架语法
C++是由C语言发展而来的高性能编程语言,诞生于1983年,兼具C语言的高效特性和面向对象等现代编程范式。它在游戏、动画、浏览器、数据库、操作系统等性能敏感领域广泛应用。C++学习曲线陡峭但威力强大,其基础程序结构包含头文件、名字空间和标准输入输出等要素。使用g++或交叉工具链编译C++程序,可直接操作硬件并获得卓越性能。作为计算机编程领域的"重装武器",C++在底层系统和算法核心开发中扮演着不可替代的角色。
2025-06-08 13:34:48
485
原创 数据结构与算法-AVL树
这棵“畸形”的二叉树虽然依旧是一棵标准的BST,但却已经明显左轻右重了,事实上这棵二叉树已经退化成了一条链表,在这棵BST中搜索某一节点的时间复杂度跟链表是一样的。这种“左轻右重”或者“左重右轻”的长短腿的情形,就是所谓的不平衡,一棵树如果不平衡,那么他的搜索性能将会受到影响,具体来讲:当树保持平衡时,其搜索时间复杂度是𝑂(𝑙𝑜𝑔2𝑛)Olog2n),当树退化成链表时,其搜索时间复杂度变成𝑂(𝑛)On),其他情况下树的平均搜索时间复杂度就介于这两者之间。
2025-05-08 20:25:47
802
原创 数据结构与算法-树、二叉树与BST
对于BST而言,插入一个节点主要是要保持其“小-中-大”的逻辑不变,因此插入的节点的逻辑可以从根节点开始,一步步寻找新节点的“最终归宿”,比如在如下BST中,要插入新节点16,最终应该插入到节点17的左孩子处。由于在顺序存储,数据元素之间的逻辑关系是用物理位置来表达的,而二叉树中每一个节点都有一个对应的标号,因此可以使用标号来作为数组的下标,但除非是完美或者完全二叉树,否则会浪费存储空间,如下图所所示。其中8是根节点,14是10的右孩子(因为二叉树是有序树,因此严格区分左右),而13则是14的左孩子。
2025-05-07 22:44:01
822
原创 数据结构与算法-栈和队列
顺序存储的队列之所以被称为循环队列,是因为可以利用更新队头队尾的下标信息,来循环地利用整个数组,出队入队时也不必移动当中的数据。从上图可以看到,链式队列主要控制队头和队尾,由于管理结构体中保存了当前队列元素个数size,因此可以不必设计链表的头节点,初始化空队列时只需要让队头队尾指针同时指向空即可。不管是顺序栈,链式栈,栈的操作逻辑都是一样的,但由于存储形式不同,代码的实现是不同的。队列是最常见的概念,日常生活经常需要排队,仔细观察队列会发现,队列是一种逻辑结构,是一种特殊的线性表。
2025-05-07 21:26:28
335
原创 数据结构与算法-内核链表
因为对每一种不同的数据,所构建出来的链表都是跟这些数据相关的,所有的操作函数也都是数据密切相关的,换一种数据节点,则所有的操作函数都需要一一重写编写,这种缺陷对于一个具有成千上万种数据节点的工程来说是灾难性的。在普通链表的节点设计中,不同的链表所使用的指针不同,就直接导致操作函数的参数不同,在C语言的环境下,无法统一这些所有的操作,这给编程开发带来了很大的麻烦,尤其在节点种类众多的场合。接着,将这样的不含任何数据的链表,镶嵌在具体要用串起来的数据节点之中,这样一来,就可以将任何节点的链表操作完全统一了。
2025-04-26 18:02:46
735
原创 数据结构与算法-容器中的双向链表
在链表中查找某个节点也是一种常规操作,但查找操作与上述的增删操作有个很大的不同,节点的比对是跟节点本身数据密切相关的,比如整型数据可以直接使用等号来判断是否一致,而字符串则需要通过特定的函数才能判断,至于结构体,则无法使用任何现成的方式去判定,只能由用户根据其实际数据去判定。由于C语言没有类,也不支持重载,受语言本身特性的限制,一般不使用第一种办法来设计通用容器,但在一些小型程序中,C语言也是可以实现通用性的,关键在于:让用户提供数据的类型。由于增删操作都涉及用户具体的数据,因此需要对之前的操作作出修改。
2025-04-17 22:02:45
968
原创 数据结构与算法-单链表
链式存储中,所有节点的存储位置是随机的,他们之间的逻辑关系用指针来确定,跟物理存储位置无关。增删数据都非常迅速,不需要移动任何数据。另外,又由于位置与逻辑关系无关,因此也无法直接访问某一个指定的节点,只能从头到尾按遍历的方式一个个找到想要的节点。由于头结点是不存放有效数据的,因此如果空链表中带有头结点,那么头指针 head 将永远不变,这会给以后的链表操作带来些许便捷。由于链表中的各个节点被离散地分布在各个随机的内存空间,因此销毁链表必须遍历每一个节点,释放每一个节点。
2025-04-15 23:03:19
1163
原创 并发编程--线程调度及优先级与信号处理
由于多线程程序中的线程的执行状态是并发的,因此当一个进程(注意不是线程)收到一个信号时,那么究竟由进程中的哪条线程响应这个信号就是不确定的,取决于哪条线程刚好在信号达到的瞬间被调度,这种不确定性在程序逻辑中一般是不能接收的。在Linux中,线程是系统的调度实体,Linux是抢占式内核,意味着线程在占用CPU运行时并不是高枕无忧的,而是可以被所谓高优先级的其他任务抢占,这就引出了优先级的基本概念。系统默认的静态优先级是0,即普通任务,如果要将程序将默认的优先级设置为非零,那么启动是必须加。
2025-04-13 09:00:00
1745
原创 并发编程--线程池
一个基本事实是,线程的创建和销毁都是需要额外的系统资源的,如果线程的生命周期很短,那么相对于实际干活的时间,来回重复创建和销毁就显得很不划算,但也不能让线程执行完任务之后耗着不走,因此就需要一个比较科学合理的布局,来管控线程,一个比较成熟的方案是:在上述情况下,将线程放入一个类似缓冲区的。线程池的基本想法,是将许多线程,放置一个池子中(实际就一个结构体),只要有任务,就将任务投入池中,这些线程们通过某些机制,及时处理这些任务。而任务都被放入一个链表,被互斥锁保护起来。
2025-04-13 09:00:00
1449
原创 并发编程--条件量与死锁及其解决方案
在许多场合中,程序的执行通常需要满足一定的条件,条件不成熟的时候,任务应该进入睡眠阻塞等待,条件成熟时应该可以被快速唤醒。上述做法实际上相当于现实生活中的立遗嘱,因为人去世之后是无法再做任何事情的,因此为了防止死亡在关键阶段意外到来,可以在提前立遗嘱,万一不幸遇到该情况就有了预案(注意,上述表述中,条件和条件量是两个不同的东西,所谓条件就是指程序要继续运行所需要的前提条件,比如文件是否读完、内存是否清空等具体的场景限定。死锁指的是由于某种逻辑问题,导致等待一把永远无法获得的锁的困境。
2025-04-12 21:07:53
894
原创 并发编程--互斥锁与读写锁
互斥与同步是最基本的逻辑概念:使得多线程间互斥运行的最简单办法,就是增加一个互斥锁。任何一条线成要开始运行互斥区间的代码,都必须先获取互斥锁,而互斥锁的本质是一个二值信号量,因此当其中一条线程抢先获取了互斥锁之后,其余线程就无法再次获取了,效果相当于给相关的资源加了把锁,直到使用者主动解锁,其余线程方可有机会获取这把锁。定义互斥锁是一个特殊的变量,定义如下:一般而言,由于互斥锁需要被多条线程使用,因此一般会将互斥锁定义为全局变量。初始化与销毁未经初始化的互斥锁是无法使用的,初始化互斥锁有两种办法:静态
2025-04-12 20:54:41
1169
2
原创 并发编程--线程的属性
由于线程属性众多,因此需要的时候不直接设置,而是先将它们置入一个统一的属性变量中,然后再以此创建线程。这些属性可以在创建线程的时候,通过属性变量统一设置,有少部分可以在线程运行之后再进行设置(比如分离属性),下面介绍属性变量如何使用。默认情况下,线程启动后处于可接合状态(即未分离),此时的线程可以在退出时让其他线程接合以便释放资源,但若其他线程未及时调用。因此,若线程退出时无需汇报其退出值,则一般要设置为分离状态,处于分离状态下的线程在退出之后,会自动释放其占用的系统资源。分离状态下的线程是无法被接合的。
2025-04-11 19:00:00
655
原创 并发编程--守护进程编写步骤
守护进程(Daemon)也被翻译为精灵进程、后台进程,是一种旨在运行于相对干净环境、不受终端影响的、常驻内存的进程,就像神话中的精灵拥有不死的特性,长期稳定提供某种功能或服务。
2025-04-10 09:00:00
1253
原创 并发编程--守护进程
在编写守护进程的过程中,会跟如下诸多概念打交道:进程组、前台进程组、后台进程组、会话、控制终端等,下图简略地反应了它们之间的关系:系统调度的最小单位是进程(或称任务,task)若干进程可组成一个进程组,若干进程组可组成一个会话,可见这几个概念都只是进程的组织方式,之所以要构造进程组和会话,其根本目的是为了便于发送信号:我们可以通过给进程组或会话发送信号,便可使得其内所有的进程均可收到信号。
2025-04-09 17:52:32
851
原创 并发编程--信号量组SEM
信号量组非常类似于停车场的卡牌,想象一个有N个车位的停车场,每个车位是立体的可升降的,能停n辆车,那么我们可以用一个拥有N个信号量元素,每个信号量元素的初始值等于n的信号量来代表这个停车场的车位资源——某位车主要把他的m辆车开进停车场,如果需要1个车位,那么必须对代表这个车位的信号量元素申请资源,如果n大于等于m,则申请成功,否则不能把车开进去。对于信号量而言,最重要的作用就是用来表征对应资源的数量,所谓的P/V操作就是对资源数量进行 +n/-n 操作,既然只是个加减法,那么为什么不使用普通的整型数据呢?
2025-04-09 17:43:38
1028
原创 并发编程--具名管道
具名管道是跟匿名管道相对而言的,从外在形态上来看,具名管道更接近普通文件,有文件名、可以open打开、支持read()/write()等读写操作。具名管道通常又被称为FIFO(First In First Out),这其实所所有管道的基本特性,那就是放入的数据都是按顺序被读出,即所谓先进先出的逻辑。先进先出的具名管道与PIPE一样不支持定位操作lseek()与PIPE一样秉持相同的管道读写特性使用专门的接口来创建:mkfifo()(匿名管道是pipe())
2025-03-16 20:59:04
384
原创 并发编程--进程间通信(IPC)概览以及匿名管道
进程间通信(IPC)概览以及匿名管道的基本逻辑、函数接口、匿名管道的读写特性、匿名管道的阻塞特性
2025-03-14 17:13:08
1567
原创 并发编程--进程与程序基础
而其余的进程,从上述pstree命令的执行效果可见,都是系统初始进程的直接或间接的后代进程。在Linux中,程序文件的格式都是ELF,这些文件在被执行的瞬间,就被载入内存,所谓的载入内存,如上图所示,就是将数据段、代码段这些运行时必要的资源拷贝到内存,另外系统会再分配相应的栈、堆等内存空间给这个进程,使之成为一个动态的实体。在Linux系统中,除了系统的初始进程之外,其余所有进程都是通过从一个父进程(parent)复刻(fork)而来的,有点像人类社会,每个个体都是由亲代父母繁衍而来。
2025-03-11 22:28:58
623
原创 C语言综合案例-简易图书管理系统(控制台输出结果)
简易图书管理系统,在控制台输出,实现添加图书、显示所有图书信息、查找图书、借阅图书、归还图书、退出系统等功能。
2025-02-26 20:55:24
504
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人