- 博客(36)
- 收藏
- 关注
原创 信号的产生和保存
信号就是操作系统对用户操作做出的反应,但它的本质就是往操作系统写入信号,这是由操作系统的结构决定的。通过修改比特位来告诉操作系统接收信号和传了几号信号。也正是因为我们身为用户无法亲自修改内核数据,所以我们需要通过操作系统提供的系统调用来让操作系统帮我们做修改操作。
2025-03-24 22:21:38
911
原创 命名管道与共享内存
命名管道也许像个懒驴一样抽一下走一下效率有点低(和共享内存比),但是共享内存快就一定好吗,未必,所以我们把它们相结合,实现又快又稳的方法进行读写,完全可以按照我们的实际情况,设置条件实现全自动读取写入。
2025-03-20 23:23:12
312
原创 软硬链接以及库的制作与原理
动态库和静态库的区别就是动态库因为不是直接拷贝进可执行程序里,所以它需要时刻能保持联系,这个联系就是能有路径找到它,可是我们写的并不是原生的动态库,所以去默认库路径下是找不到的,那么我们就通过软链接来和原生库建立联系,进而可以让操作系统找到我们的库。而如果在一个目录下同时存在动静态库,那么操作系统会优先使用动态库,这时候你要是没链接上,那就报错了。你要是非要在动静态库同时存在的情况下用静态库,那你只能在最后加个 static 修饰一下了。
2025-01-22 01:14:33
891
原创 磁盘与库之间的结构关系
在磁盘被分区之后,每个分区又有多个分组,分组之下又有多个inode和数据块,这些inode和数据块都被编号了,且在一个分区里编号是唯一的,也就是说如果组1有一个编号1,那么在这个分区里就不会再出现第二个编号1。但是如果出了分区,别的分区是可以有的,所以node和数据块不能跨分区。
2025-01-21 01:50:41
640
原创 Linux的进程替换以及基础IO
上一篇草率的讲完了进程地址空间的组成结构和之间的关系,那么我们接下来了解一下程序的替换。首先,在进程部分我们提过了,其实文件可以在运行时变成进程,而我们使用的Linux软件其实也是一个进程,所以进一步来说,我们在Linux里写的一切文件都是子进程,而进程的替换一般就发生在父子进程之间,我们先了解一下它的特性,首先如果使用了进程替换,那么它会在替换处直接取代原有代码,覆盖式的运行,所以并不会产生新的子进程,也不会减少进程。所以一旦替换成功,之后的代码都会被覆盖不会执行。
2024-12-30 20:38:30
800
原创 Linux进程地址空间
最后,对于物理地址和虚拟地址做一个总结,首先操作系统先从磁盘加载数据到物理地址上,然后就知道它占多大空间了,所以在虚拟地址空间开辟相同大小的空间,然后把双方的地址一一填入页表组成映射关系。那么现在,物理地址有没有划分已经不重要了,就算它是乱存的,爱存哪存哪都不会影响我们了,因为我们的虚拟地址空间会通过页表的映射关系,把它从无序变为有序。而野指针就是只有虚拟地址但是没有对应的物理地址,找不到所以野指针。
2024-12-24 16:14:55
707
原创 智能指针总结
最后shared_ptr如果按照咱的结构创建其实有一些隐性问题,比如每次new计数的时候都会产生四个字节的内存碎片,少了还好,我们要是多了,那内存给这些计数搞的断断续续的,所以大佬们像make_pair一样给我们提供了一个make_shared,它一次性开齐空间,专门在头部给我们留了四个字节的计数空间,同时为了方便我们知道这个计数到底有没有人在用,shared_ptr还支持了转换为operator bool的重载。咱可以在需要的时候判断它是否有人在用。
2024-12-18 02:08:57
786
原创 初识Linux进程的概念
所谓的进程,笼统的理解起来就是系统正在执行或者未来将要执行的代码,这些代码实际上是以文件的形式保存在外设里的,只有运行的时候才会加载到内存,这时候它就从文件转化为了进程。我们也可以同时运行多个进程,实际上操作系统也是这么干的,就像我们手机电脑同时打开多个应用一样,当然为了防止这些进程打起来,有一个类似后台的东西集中管理这些进程。
2024-12-11 23:44:28
842
原创 C++11新特性
c++11更新的首当其冲就是对初始化开刀,它觉得前面版本各种初始化太乱了,所以直接让{}可以初始化一切,并取名叫列表初始化,和类的初始化列表不一样嗷。而这里比较出名的就是 initializer_list 它本身是一个类模版,会自动识别{}的列表给对象的操作,而一旦识别,它本质上就是个数组,里面两个指针,一个指向头,一个指向尾,然后一个个拷贝进去,所以它支持范围for。但是请注意区分。
2024-11-08 22:56:09
1007
原创 哈希及其封装实现unordermap和set
哈希和之前的红黑树的区别就是,它是通过映射关系来找到目标的,可以把它想象成之前排序的计数排序,那其实就是哈希的一种方法,叫做直接定址法。对于比较集中的数据,它只需要开一段区间,然后通过映射关系把数据存到对应的位置上就可以了,但是它的缺点也很明显,如果数据很分散,那它的消耗就很大并且效率也很低。同时,对于非整型的数据它还多了一个转换的过程,否则也无法存入。
2024-10-29 02:14:01
715
原创 红黑树底层逻辑及代码实现
想要学习红黑树之前,我们需要先了解它的规则,也许从名字可以先感受到它的第一点规则,它不再像AVL树一样引用平衡因子来判断平衡,而采用红色和黑色来判断平衡。首先它的根节点一定是黑色,且如果一个红色节点的孩子必须都是黑色,也就是说一条路线上不会有连续两个红色节点,且一棵红黑树任意路线上的黑色节点的数量是相等的。也就是说,只要没有连续红节点且每条路径黑节点数量一致就行。这样最短路径就是全黑 X,而最长路径就是黑红交替 2X。但是最短和最长路径不一定存在,大部分情况是介于两者之间。
2024-10-16 01:05:27
967
原创 AVL树的实现
avl其实并不算单独的树,它和map、set是密不可分的,逻辑上大体是相同的,不过它多了一个平衡因子的概念,也就是说,它是依靠平衡因子来决定自己是否失衡需要调整的。我们可以看到它的结构和map、set并没有什么太大不同,不过多了一个bf的平衡因子和父节点。平衡因子的作用是为了决定自己是否失衡,而父节点则是决定了如果失衡,可以向上调整,类似于单链表进化成了双向链表。甚至于它的插入在不涉及旋转的部分都和前面的map/set一样。还是老一套的插入,然后更改位置,只不过多了一个parent的修改。
2024-10-05 02:19:47
989
原创 Map+Set
我们前面接触过的string、vector、list这些都算序列式容器,它们都有一定的关联性,即使随便换位置也无伤大雅,因为是它们靠位置顺序来保存的。但是今天的MapSet就不是了,它们算关联式容器,两个位置之间有紧密的联系,如果随意交换一下,那它们的结构就被破坏了,因为它们是靠关键字来保存和访问的。
2024-09-27 00:00:48
786
原创 二叉搜索树
二叉搜索树其实和二叉树差不多,都是一颗树,但是它有点排序的作用,因为比根节点大的在根的右边,比根节点小的在根节点的左边,以此类推,这样下来只要走一个高度次就能确认一个数在不在里面,不过这是最好的情况,因为如果是有序或者一直来大于或者小于根的值,那么这棵树虽然还是搜索树,但是其实已经失衡了,这样效率就会变低,所以它还是带点缺陷的。这就是它的基本结构,需要注意的是这里的节点暂时是不允许重复且不能插入的,后面会有multi版本支持重复,但是还是不允许插入,因为它害怕结构被破坏。
2024-09-25 03:33:24
447
原创 多态的概念
所谓的多态其实就是多种形态,它又被分为编译时多态(静态多态) 和 运行时多态(动态多态)。静态的多态其实就是之前的模版和函数重载,今天我们主要讲动态的多态。所谓的动态多态其实就是相同的函数,完成不同的功能。这就实现了明明都是用A&类型的参数,但是调用的结果不一样的情况出现。要达成这一点其实有两个地方需要注意,首先,必须是父类的指针或者引用,不然切片切不好。其次,需要父类和子类函数的返回类型、函数名、参数类型都要一样,这样,在前面加上一个virtual就可以达成虚函数的重写了。
2024-09-12 23:06:16
837
原创 deque和优先级队列
咱学完栈和队列之后,又了解到了vector和list,更深入的了解到了它们各自的优势,那么有没有可能结合它们的优点摒弃弱点呢,其实是有人这么试过的,不过咱还在学vector和list就证明他可能没成功,不过并不影响我们理解它。
2024-08-04 17:38:53
318
原创 C++List基础功能模拟实现
这里需要注意的是,我们想实现const类型的迭代器,并不是直接const iterator就可以的,因为如果这么写,我们反而是限制了迭代器不能修改,那么我们的++--都将无法实现,所以我们的const应该是要加在类型前面的,而我们把这一步交给了使用者,他传的什么类型const我们就用它实例化出来。在list的基本功能前,我们还需要一些准备来辅助,但是链表和顺序表不同,它无法通过下标和[]来访问内容,所以链表的迭代器就有些麻烦,这需要我们再定义一个结构来实现。迭代器都解决了,那么之后的功能实现就很简单了。
2024-08-01 18:30:50
270
原创 C++vector基本功能及模拟
我们可以把vector简单的理解为顺序表,它的数据是连续的,也可以通过下标去访问,让我们先来了解它的结构。而它的实现是以模版的方式实现的,为了可以满足各种类型的使用。也因此,它的迭代器类型也有所改变。而它的构造和析构也很简单,因为是连续的,只要确认头尾即可。而它的拷贝构造就有点小坑。它的基本结构就是这些,那么接下来我们来看看它的基本功能。
2024-07-27 17:34:23
147
原创 C++ 模版和STL
在此之前,我们遇到相同功能但是类型不同的函数可能会很麻烦,即使有了重载,依旧要写很多。祖师爷应该也有这种困扰,所以他整了个模版出来,模版的功能就是可以自动替换类型,只要我们写了一个模版,那么所有类型都能用,只需要写一次就好了。这就是函数模版的写法,如果咱的参数都是一个类型的,那就只要定义一种就好。之后我们正常调用函数传参,模版就会被触发,他会让编译器去生成这个类型的函数,这个过程叫模版实例化。需要注意的是,如果我们只写了个一种类型的模版,但是参数传的却是不同类型。
2024-07-23 17:19:47
289
原创 C++中结
这个其实就是在构造函数自动调用的时候给它一个初始值。它的格式就是: : 成员名(值或表达式)注意是不需要写分号的,直接回车就好,如果还有想初始化的,那就加上一个逗号继续写就好了。同时,每个成员只能初始化一次,不能说我初始化两遍。有一些情况下是必须用初始化列表的首先就是const修饰的变量,因为它只能在定义的时候赋值,而类里是声明,所以它能在初始化列表定义进一步确定了初始化列表的时候就是定义。
2024-07-14 16:39:03
945
原创 队列与栈之间的联系
当我们学栈或者队列的时候,也许有想过我能不能用另一个来实现对方,看似完全相反的性质,实际上确实可以通过某些方式来实现,但是可能看起来很没有意义,不过可以让我们对栈和队列的理解更进一步。
2024-05-13 08:35:35
311
1
原创 关于单链表在使用中产生的常见问题
简单科普一下什么是带环链表,正常的单链表结构是线性的,他的最后一个节点指向的是空,带环则是最后一个节点指向前面的某一个节点,形成一个环形,比较经典的就是约瑟夫问题,它就是一个带环链表,而我们想要判断它是否是带环链表,需要用到我们的快慢指针。
2024-04-30 09:13:44
442
2
原创 贪吃蛇的实现
首先我们的食物它的x得是随机2的倍数,还不能比我们的框架大,其次他不能直接出现在咱蛇肚子里,如果这些条件都满足,那么我们把光标定位到这个地方打印食物,再我们把坐标传到蛇属性的结构体里的食物。而光有蛇是不够的 我们还得设置一下蛇的属性。
2024-04-20 10:40:48
915
原创 编译与链接
一般来说我们写的代码只是一堆字符和数字,计算机和操作系统并不以能直接识别出来,所以有了编译,在ANSI C实现下一般有两种环境,一种叫编译环境,在编译环境中,可执行的源代码会被转化成可执行的机器指令(二进制指令),第二种叫执行环境,用于实际执行代码。编译环境一般依赖于我们的编译器比如VS 而执行环境依赖的是我们的操作系统。
2024-04-20 08:37:37
215
原创 编译和链接
一般来说我们写的代码只是一堆字符和数字,计算机和操作系统并不以能直接识别出来,所以有了编译,在ANSI C实现下一般有两种环境,一种叫编译环境,在编译环境中,可执行的源代码会被转化成可执行的机器指令(二进制指令),第二种叫执行环境,用于实际执行代码。编译环境一般依赖于我们的编译器比如VS 而执行环境依赖的是我们的操作系统。
2024-03-26 10:59:14
388
原创 文件的读写
当我们操作完了之后想要看看它的结束到底是寿终正寝的结束,还是中途出了问题暴毙的结束,这时候我们就需要用到feof和ferror函数了,在讲之前我们又需要了解,在 流 里其实有两个参数,一个就是feof,另一个则是ferror,如果它是正常的结束那么feof的值则会定为正数,不然就是返回负数。同理,我们用ferror判断一下,如果返回的是正数,那代表它确实有问题,不然就代表没问题,返回的是0。
2024-03-26 09:27:54
742
原创 malloc realloc calloc以及free
当我们创建变量时,空间也许会太少也许会太多,造成空间的浪费,这时候我们就可以使用malloc来动态的在堆上开辟空间了,如图所示。此时我们就在堆上开辟了10个整型大小的空间,把首元素的地址存放在ps里。我们再来看看malloc的参数。首先void*表示他的类型可以根据使用者的需求来自定义,需要什么类型就强转它什么类型,size_t size则表示它的长度必须是无符号整型,毕竟不能开辟负数的空间,而开辟的空间大小也由我们自己决定。
2024-03-24 03:36:32
819
1
原创 结构体联合体和枚举吧
需要使用关键字 struct,加上创建类型的类型名 比如s1 然后定义结构体的成员,至少有一个,之后就可以使用结构体类型 struct s1来创建变量了,如struct s1 stu。而结构体的传参有两种方式,第一种就是形参,不改变结构体的内容,比如打印结构体成员的值,只要结构体名.结构体成员即可访问。而另一个则是实参,将结构体的地址传入,以上面创建的结构体为例,只需要加上一个 * 号即可,struct s1* 此时只需要结构体名->结构体成员就可以打印出来。
2024-03-19 08:56:37
631
原创 关于整数和浮点数在数据中的存储
关于整数在数据中的存储比较简单,分为有符号整数和无符号的整数,他们的存储都是转化为二进制存储,而首位是符号位,但是在无符号的整型中他不会被识别为符号位,而是当成正常的二进制位运算就会出现图中所示的情况。我们再顺便提一嘴char类型,因为是一个字节八个比特位,所以会出现整型提升的情况,进而导致他的值可能产生一些变化,比如按理说应该是产生129这个值,但是为什么会变成-127呢首先我们需要知道原反补的区别,其次需要知道当以整型打印字符类型时会产生整型提升和截断。
2024-03-16 13:54:32
332
1
原创 关于str和mem系列的部分讲解
strstr的意思就是查找,在一段内容中查找是否有我们需要的目标,所以我们首先要记录好两个参数的起止位置,因为之后查找将会改变他们的指向,而当两个参数都指向'\0'时就代表查找完了,而如果两个参数相等,则代表我们可以继续往后查找,如果不相等则只有第一个参数往后走一步,当第二个参数等于‘\0’时那就代表我们可以在第一个参数中找到他,那么我们就返回我们找到他的地址star。
2024-03-14 23:35:36
364
1
原创 关于C语言的指针捏
当你学习C语言一段时间之后亦或者开始学习时,你就听说过指针这个名词,但可能并不了解他到底是什么,听我来给你解释。你是否在使用函数时为无法改变你所传入的变量无法改变而烦恼,这时候只需要使用一下指针,你就可以做到函数内外变化都影响数据。而一级指针的写法也很简单,只需要一颗 * 就行了具体的写法是:原数据类型* 指针名 = &原数据名比如 int a = 10;那么他的一级指针写法就是 int* pa = &a;
2024-03-05 10:03:53
689
1
原创 一个简单的扫雷游戏
以上就是这个扫雷的实现,其实关键点就在于想明白我该怎么定义这个棋盘的大小,需要考虑越界的问题,而埋雷就是生成随机数下标然后把下标的值改掉,再定一个改的上限,来确定随机几雷,只有排雷的部分可能复杂一点,因为需要考虑的问题比较多既要考虑运行的bug还要提防找茬,但主体还是判断输入的值和下标比较,如果输入的值不等于雷的值那就说明我没踩雷,再进入下一个步骤,计算周围的雷数,再把打印出来看的二维数组的下标值改成周围的雷数,方便我们继续猜,最后定义一下胜利条件,扫雷就算完成了。首先我先贴入代码,之后再讲解具体的实现。
2024-01-30 10:00:44
465
1
原创 关于C语言中分支和循环的一点浅显理解
而循环则是在两个区间中重复做一件事情,直到满足他的跳出条件,最常见的就是while和for循环,这两个循环用法大抵相同,但是用起来个人感觉for循环会更方便一点,毕竟while循环如果稍微长一点,其中变量的变化寻找起来会非常麻烦,而for循环就不一样了,它的所有变化基本上都可以在一行内解决,即使是非常长的一串代码,他的变化也可以比较简单的看出来。而在以上循环中,如果一次判断不够,那么还可以加入分支语句,从而更好地执行循环的条件,同时if也可以借助循环的特性减少时间和空间的浪费。
2024-01-24 09:54:39
366
1
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人