- 博客(43)
- 收藏
- 关注
原创 Linux:环境变量
如果我们输入echo $PATH命令,会看到多个由冒号分隔的路径(例如/usr/bin:/bin:/usr/local/bin等)。上图我们通过 file /usr/bin/ls 来输出ls命令的属性,可以看到ls也是用c语言写的程序,所以我们在使用ls -a或ls -l 时候程序都会对传入的的命令进行命令行分析,来决定输出的文件形式。,一张命令行参数表,一张为环境变量表。上图可以看出,argc代表的是传入了多少个命令行参数,而argv则是传入的命令行参数,而我们在命令行上输入的内容都会存入argv中。
2025-04-02 15:38:43
1076
原创 Linux:进程状态总结
在PCB中有一个struct结构体,我们先暂称为list_head,这个结构体里就存放着prev和next,然后next就指向下个PCB的next,prev就指向上个PCB的prev,而不是指向首字节,对于为什么要这么操作,是因为在操作系统在,task_struct里存放着许多list_head。所以通过上图的理解,对进程状态的管理,其实就可以转化为对链表的管理,进程的状态变化,本质是在不同的队列中进行流动,本质都是对数据结构的增删查改。所以在操作系统中,一个进程的结构体,可以同时属于多种类型的数据结构。
2025-02-15 15:37:09
742
原创 Linux:进程概念详解
分析上图代码,一开始我们输出当前进程的PID和PPID,接着通过fork进行创建子进程后再次打印PID和PPID,我们会发现此时除了第一条打印的语句我们理解,还多了一条打印的语句。并且观察PID,会发现PID为 19215 的PPID正是一开始打印的PID 19214,那么就能够证明了fork会创建子进程,并且子进程会继承父进程fork()之后的代码,所以会打印两句输出语句。通过上图运行可以看到,我们一直运行mycode的文件时,它的PID是依次递增的,但一直不变的是PPID。但进程并非仅仅是数据和代码。
2025-02-15 01:05:58
700
原创 C++进阶:Hash表总结及模拟实现unordered_map与unordered_set
简单来说,哈希表就是将一些没有规律的值存储在一段有序的区间内,通过哈希函数将数据映射到这个区间中的某个位置,从而实现快速查找。在C++中unordered_map与unordered_set底层都使用的是哈希表,所以对unordered_map与unordered_set封装本质上也是是对哈希表的更上层的封装,以及对原哈希表结构进行优化处理。如果关键字是 [a, z] 的小写字母,我们可以开一个大小为 26 的数组,每个字母的 ASCII 码减去 a 的 ASCII 码就是数组的下标。
2025-02-04 16:06:25
445
原创 C++进阶: 红黑树及map与set封装
红黑树整理与AVL树类似,但在对树的平衡做控制时,AVL树会比红黑树更严格。AVL树是通过引入平衡因子的概念进行对树高度控制。红黑树则是对每个节点标记颜色,对颜色进行控制。
2025-02-02 18:20:21
675
原创 Linux:初识进程
分析上图代码,一开始我们输出当前进程的PID和PPID,接着通过fork进行创建子进程后再次打印PID和PPID,我们会发现此时除了第一条打印的语句我们理解,还多了一条打印的语句。并且观察PID,会发现PID为 19215 的PPID正是一开始打印的PID 19214,那么就能够证明了fork会创建子进程,并且子进程会继承父进程fork()之后的代码,所以会打印两句输出语句。gval是一个全局变量,父子进程都可以看到,当我们fork之后,子进程会开始对gval值进行修改,而父进程只对gval值进行查看。
2025-01-13 09:51:53
711
原创 C++进阶:AVL树
一:AVL树的介绍一:AVL树的介绍AVL树(Adelson-Velsky and Landis树)是一种自平衡的二叉搜索树(BST),它通过在每个节点维护一个平衡因子,确保树的高度差不超过1,从而使得树的操作(如查找、插入和删除)的时间复杂度保持在 O(logn) 的水平。因为我们在普通的二叉树插入的时候,也许会出现下图这种极端的情况:那么如果此时搜索5或4的时候时间复杂度就为O(N)。那么为了解决这种情况苏联的两位数学家就发明了AVL树。在AVL树树中有个关键的概念,那就是。
2025-01-05 16:50:13
1007
原创 力扣二叉树题解含思路(C++实现)
这里小编想多一嘴,为什么cur会回到6节点,因为我们采用的方式是递归,递归函数会进行压栈,当我们压入4节点的函数栈帧时候,6节点栈帧会保存在4节点下面,而当4节点函数结束时候,此时函数栈帧进行弹出销毁,6节点就是栈顶,所以此时cur的值就是6.而且因为递归的关系,就算指向改变了也不会影响中序遍历的顺序,因为已经将顺序全部压入栈中了。其实这题如果忽略题目要求就很简单,因为是标准的搜索二叉树,就通过中序遍历存入list题目就解决了,但小编应题目要求,空间复杂度为O(1)及在原树上操作。
2024-11-10 19:20:32
747
1
原创 初见Linux:基础开发工具
前言:这篇文章我们将讲述Linux的基本开发工具,以及讨论Linux的生态圈,最后再了解vim开发工具。Yum:YUM(Yellowdog Updater Modified)是一个在Linux系统中用于管理软件包的工具,特别是在基于RPM(Red Hat Package Manager)格式的发行版上,如Red Hat Enterprise Linux、CentOS和Fedora等。YUM允许用户轻松安装、更新和删除软件包,并自动处理依赖关系。在Linux下安装软件的方法。
2024-11-09 15:01:12
660
原创 初见Linux:权限篇
从上图我们取消了dir目录中拥有者的w权限,接着进入dir,ls后发现dir目录下拥有一个test.txt文件,此时将test.txt文件进行删除发现没有权限。我们可以看到此时用户为dog,test.txt对于dog用户只用r的权限,但从上图dog用户可以直接删除test.txt文件,这是为什么,是不是不符合常理。因此可以排除r权限,那会不会是w权限呢?在Linux中的访问权限中,有三种不同角色的访问权限,分别问拥有者的访问权限,所属组的访问权限,以及other(其他人)的访问权限。很好的做到了用户隔离。
2024-10-28 01:01:56
621
原创 C++:多态
从上图调试信息可以看见,主函数的基类b与派生类的c 和 fun1()里的基类b1与派生类的c1它们的虚函数表指针都是同一个地址,那么就可以断定,虚函数表不是单独存在的,而是对象共用的。接着我们要取虚函数表地址,经过之前的例子我们可以知道存虚函数表是存在于对象的首地址,所以 *((int*)&p)的意思是先&p取到p的地址,然后(int*)&p将p的地址强制转换成int*类型,最后在解引用获取到地址的数值。因为在系统内存中是不会考虑你是什么类型的地址,存的都是二进制的0101,所以只看你的内存地址对不对。
2024-09-27 14:40:49
621
原创 C++:继承
但你总不可能是老师的时候,你的年龄,性别是一套信息,而当你是学生的时候你的年龄性别又是另一套信息,这不符合常理。继承嘛,继承遗产是继承,继承家业也是继承。所以如下图因为老师和学生同时都拥有人的属性,所以通过继承,让老师和学生都继承Person类,就相当于他们都拥有了Person的成员变量,而他们自己的成员变量又是独有的。那么从上图可以看到,创建了一个int类型的b引用去引用a,这里并不是直接引用a而是引用中间的临时变量,而临时变量是具有常性的,所有普通的引用不行,必须有经过const修饰的引用才行。
2024-09-18 11:31:42
729
原创 C++:关于反向迭代器的学习分享
反向迭代器的begin位置对应容器的end位置,反向迭代器的end位置对应容器的begin位置,为了方便理解,下图是一个list的begin位置以及end位置,那么相对于的就是list的反向迭代器的rbegin位置以及rend位置。那么反向迭代器的operator++()与 operator--()函数会调用正向迭代器的operator--()与operator++(),并且内部都是对正向迭代器的函数复用操作。那么反向迭代器的初始化其实就是通过传入的正向迭代器的参数对反向迭代器里的it进行初始化。
2024-09-03 19:16:28
704
1
原创 C++:模拟实现list
上篇文章简单介绍了list的接口使用,那么这篇文章将通过模拟实现list以及常用接口来更好的去理解list类,观看本章前将默认读者对链表的数据结构有初步的了解。list在库里是一个带哨兵位的双向循环的链表结构,并且list是一个类模板并不是一个指定的类型,所以在模拟实现时需要写成模板。
2024-08-31 11:35:52
1337
原创 C++:list篇
我们可以看到,当erase后如果在对it迭代器进行操作,vs则会进行强制检查(根据编译环境不同,处理环境也不相同),则不允许再继续使用,如果想继续使用的话,C++同样也给出了方案,可以看到erase会返回一个迭代器,而返回的迭代器就是下一个链表的位置。那么从上面的begin与end可以看出,使用迭代器遍历list,仅仅只需要解引用迭代器以及++迭代器就能取得我们想要的值与让迭代器走向下一个节点,那么有没有想过一个问题,list的迭代器与vector这种类似数组的迭代器底层是否是一样的呢?
2024-08-27 21:01:50
1647
原创 C++:vector篇
从上图可以看出,一开始创建了vector<int>类型的v1,并且通过push_back函数(这个函数后面会说明),对v1进行插值,接着又创建了v2并以v1为拷贝对象使用拷贝构造,使用范围for打印结果可以看出,v2把v1的数据进行了拷贝,并同时也进行了扩容的操作。从上图看到,在vector中的成员变量不像之前的sting,他采用了三个类模板的指针变量,_start(开始) , _finish(完成), _end_of_storage(结尾的存储位置),分别对应了str的指针,top以及capacity。
2024-08-24 10:46:01
952
原创 C++:模拟实现string
为了更好的理解string底层的原理,我们将模拟实现string类中常用的函数接口。为了与std里的string进行区分,所以用命名空间来封装一个自己的strin类。
2024-08-19 16:51:43
1050
原创 C++:string类篇
从上图可以看到,我们创建了一个string类的iterator(迭代器)变量it,并用s1.begin()函数的返回的迭代器值赋值给it,接着就类似指针的操作,将it解引用并循环输出值。2.string& append (const string& str, size_t subpos, size_t sublen):截取string对象中的一部分内容到自身内容后.1.string& append (const string& str):使用已有的string对象数据拓展自身对象数据。
2024-08-19 11:45:08
1034
原创 C++:初阶模板
类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟< >,然后将实例化的类型放在< >中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。从上图可以看到,模板仅仅至少充当一个中介的角色,当给模板传入实参,编译器会根据实参的类型去推演自动生成对应类型的函数,可以从下图的汇编码能更好的证明这一点。可以看到如果按照之前的方式仅仅通过访问限定符去定义类是完全不行的,在 C++ 中,模板类的成员函数定义必须指定模板参数,因为这些成员函数的定义是在模板类的上下文中进行的。
2024-08-14 22:25:23
515
原创 C++:内存管理
通过任务管理器可以看到此时vs的运行窗口直接占用了2G左右的内存就不动了,说明此时堆上已经没有空间申请,当程序继续运行也没有发生而是捕捉到了编译器抛出的异常,输出bad allocation(糟糕的分配),提示程序员问题的出处。int* p1 = (int*)malloc(sizeof(int)):因为C++是C的延申,所以C++兼容C语言,在C++里也可以使用C语言的代码,malloc从堆中分配了一个int类型的空间,这个大家都熟悉.除非给每个对象都传入参数。p2指向的空间是通过new分配来的。
2024-08-13 09:27:10
945
原创 C++:类与对象(中)
需要释放的资源成员都是自定义类型,不需要写析构,前提是自定义类型里写了析构函数,编译器会自动调用自定义类型成员的析构函数,这点与上面的构造函数类似。这里就需要额外创建一个对象,先保存d1的值,再让d1+1,并且因为这个额外的对象是一个局部对象,如果传引用返回就会造成野指针现象,必须穿值返回。,那么此时传d2进成员函数Print就不会发生报错,权限也只是平移,那么在传d1的时候也同样不会报错,权限可以被缩小这种机制有助于增强代码的安全性和可读性,确保对象在被标记为const时保持不变。
2024-08-10 09:00:15
1408
原创 C++:类和对象(上)
A1类的对象a与A2类的对象b通过sizeof计算出它们的大小都为1,这是为了确保每个对象在内存中占有独立的空间,即使是空类也需要有独特的地址。此时将代码进行调试并转到反汇编指令时发现,d1.Init()与 d2.Init()都call向同一块空间,那么也就说明d1的成员函数与d2的成员函数是共用的,所以类成员函数的是存放在一个公共的代码区域段的地方,那么这样做也是为了节约空间。类中的函数称为类的⽅法或者成员函数。类同样拥有自己的域,类的所有成员都在类的作⽤域中,同时类域不影响变量⽣命周期。
2024-08-06 18:19:45
1049
原创 C++篇:入门(2)
而a的值被修改为20,可以看出在b=c这步操作里,是将c的值赋到了别名b中,而b的本体其实是a,修改b的值同时也是修改a的值。然后,通过返回k的引用来返回一个整型的引用,而因为k是函数内的局部变量,而当函数进行执行完则进行销毁,此时k的引用指向一个已经不存在的内存位置,使用引用进行返回相当于跨过了将值传递给临时变量的步骤。因为返回的是参数,而函数在返回的时候并不是直接返回值,而是会通过一个临时变量将k的值存储起来可能是寄存器的存储,当函数栈帧销毁时,再把存储的值赋给ret1,此时ret1的值是安全的。
2024-08-03 19:41:01
1166
原创 C++篇:C++入门基础(1)
上图代码,小编创建了一个int类型的全局变量rand,并在主函数里尝试输出rand,此时可以看出函数在编译的时候就进行了报错,给出的理由是rand重定义,表明rand在这里是一个函数,而自己又给rand定义成了一个int类型的全局变量,那么在c++的编译是时候是不会通过的。如上图,在函数Add中定义了三个形参a,b,c。C++⽀持在同⼀作⽤域中出现同名函数,但是要求这些同名函数的形参不同,可以是参数个数不同或者类型不同,通俗的来说,函数重载就类似于,有很多个同名同姓的人,但每个人都是不同的个体。
2024-08-02 13:43:42
1320
原创 数据结构——排序(C语言版)
a[hole] = a[left]:将找到的大于基准值的元素移到之前坑洞的位置,即hole所指向的位置。这样,prev指向的是当前已经处理好的小于基准值的部分的最后一个元素的位置。希尔排序法的基本思想是:先选定⼀个整数(通常是gap = n/3+1),把 待排序⽂件所有记录分成各组,所有的距离相等的记录分在同⼀组内,并对每⼀组内的记录进⾏排序,然后gap=gap/3+1得到下⼀个整数,再将数组分成各组,进⾏插⼊排序,相当于在插入排序之前对原数据进行一个预排序,当gap=1时就相当于 直接插⼊排序。
2024-07-31 13:43:08
1080
原创 数据结构——链式二叉树(C语言版)
根据顺序,从根开始并没有先输出值而是先开始递归根的左子树,从上图的左子树的递归展开图中,当节点递到3的左子树(NULL)时,先输出NULL接着开始归,重新归到3节点时输出3,接着递归3的右子树(7),并重复步骤,当1的左子树递归完后就输出1节点的值并执行递归1的右子树。想要计算二叉树的深度,就要计算根的左子树与右子树的深度,将左子树的深度与右子树的深度进行比较,并返回较大值,如上图,通过递归展开,会得知左子树的深度为3,右子树的深度为2,最终返回左子树的深度+1,所以上图二叉树的深度为4。
2024-07-27 23:23:54
918
1
原创 数据结构——堆(C语言版)
第三步:在循环里创建一个if语句,如果父亲节点的值小于孩子节点,那么就进行向上交换,孩子变父亲,父亲变孩子,然后将父亲位置赋值给孩子,继续向上找父亲节点位置进行循环判断是否交换,如果不需要交换(父亲节点>孩子节点)就直接退出循环。从上图可以发现,在堆顶(数组下标为0)的位置中存放的数据,是这个堆中最大的值,并且堆中某个结点的值总是不大于其⽗结点的值,我们将这种堆称之为大根堆。从上图可以看出⼆叉树不存在度⼤于 2 的结点 ,并且⼆叉树的⼦树有左右之分,次序不能颠倒,因此,二叉树又是一个有序树。
2024-07-22 21:50:03
931
1
原创 数据结构——栈与队列(C语言版)
一:栈栈的概念:栈(Stack)是一种常见的数据结构,它基于后进先出( Last In, First Out, LIFO)的原则工作。这意味着最后进入栈的元素最先被移除,而最先进入栈的元素最后被移除。栈通常有两个主要操作::将元素添加到栈的顶部。:从栈的顶部移除元素。栈的特性决定了:栈只能访问栈顶的数据,并遵循着先入后出的原则,如果像访问栈底数据,只能将栈上剩余的数据全部依次弹出,不能去遍历,否则会破坏栈的性质。
2024-07-18 13:50:41
970
1
原创 浅谈力扣138题——随机链表的复制
可能有读者会有疑惑,那可不可以直接遍历原链表节点的val值,让原链表random指向的节点中的val值去匹配新链表节点random指针的指向的节点中val值。因为要返回的是全新的的链表,链表中的每个节点都是需要去申请来的,所以,原链表节点中的random指针指向的节点,与新链表的节点中的random指向的节点是毫无关联的。,唯一相同的只有他们的next指针都是指向下一个链表,所以新链表与原链表是毫无关联,不能单纯的通过原链表的random节点去匹配新链表的random节点。这里一定要理解,新链表的地址。
2024-07-13 00:09:09
724
原创 关于力扣——环形链表|| 的一些见解
通过题目给出的示例图,我们可以得知,链表的尾节点指向前面链表的其中某一个节点,这个节点可以在头可以在尾也可以在中间,而我们需要找到链表尾节点的下一个节点及入口循环链表的入口点。当slow走到入口点时,假设此时fast的位置在图中标记的地方,这里需要知道由于slow只走一步并且fast只走两步,所以slow。一个指针从起始点开始走,一个指针从相遇点开始走,这两个指针会在入口点相遇。一个指针从起始点开始走,一个指针从相遇点开始走,这两个指针会在入口点相遇。,这道题的意思是要找出环形链表的入口点。
2024-07-11 14:41:34
539
2
原创 数据结构——顺序表(C语言版)
顺序表(Sequential List)是一种基本的数据结构,用于存储线性表(元素之间有序排列的数据集合)。顺序表的特点是元素在内存中连续存储,每个元素占用固定大小的存储单元,通过元素在内存中的相对位置来确定其逻辑顺序。从概念中可以看出顺序表和数组是很相似的顺序表与数组的区别:顺序表与数组的相同点都是在内存中连续存储,但顺序表并非是数组,只是顺序表的底层和数组一直这里简单画了一个图,我们将数组看作是一个普通衣柜,顺序表看作是一个折叠衣柜。
2024-07-11 00:38:30
955
原创 C语言关于预处理的基础介绍
在这个表达式中,(5++) 和 (4++) 会被进行比较,比较完再经行后置++操作,这里5与4先进行比较,接着5++变成了6,4++变成了5,而因为5>4,返回的是第二个表达式(a),而a这里会被替换成(6++)因为刚才5已经变成6了。从上图示例可以看出 我的test.c的文件只有#include"head.h"的文件包含,编译器会在源文件下的目录进行查找头文件,如果该文件未找到,编译器就会像查找库函数头文件一样在标准位置下查找头文件。可以看到a和b的输出是相似的,此时可以想到是不是可以用函数来进行简化。
2024-06-06 14:41:18
2053
原创 C语言编译与链接的基础知识
通过上图示例,小编在一个项目里面创建了两个.c的文件,在链接的过程中,text文件里的main标识符与Add标识符会进行符号解析,将这两个标识符解析成地址后形成一个符号表,又因为extern Add 声明了Add函数是一个外部函数 所以Add的地址为0地址,接着解析add文件里的Add标识符,形成符号表,最后将两个符号表进行合并,发现在text文件里声明的Add标识符刚好与add文件里的Add标识符对的上,那么这两个标识符地址就进行重定位,最终合成合成了一个新的符号表,那么程序就会继续执行下去并不会报错。
2024-06-05 19:15:41
1785
原创 C语言关于文件的一些基础知识
从上示例可以看到,刚才将字母a到z放入文件后,这次将pf的指针再次指向刚才的文件,并且用”r“读取的形式来操作文件,定义了变量ch,通过while循环,运用fgetc函数每一次读取得到文件中单个的数据,并且打印出来。文件指针是一个特殊类型的指针,用于跟踪程序中打开的文件。fseek(pf,2,SEEK_SET)表示将文件的光标从pf所指向的文件中的文件开头(SEEK_SET)开始向右偏移两个位置,此时光标指向了数据c的位置,再通过变量ch将数据c打印出来,此时文件光标已经指向了下一个数据d的位置。
2024-06-03 00:43:40
1990
原创 想要初步了解指针?看这篇就够啦!
从上图示例可以看到 arr的地址为00BEFE98,将arr+1及加四个字节的长度,那么&arr+1与arr的首地址地址相差20个字节,得到数组末尾的地址,而数组指针p取得arr的地址后,将p+1,得到的也是数组末尾的地址。从上图可以看出:ptr指针一开始指向的是arr首元素的地址,将ptr+2,越过八个字节,得到数值3的首元素地址,接着用“ * ”操作符,将int类型的指针解引用访问后4个字节的长度,得到数值3。换句话说,二级指针存储的是一个指针变量的地址,而这个指针变量本身又存储着另一个变量的地址。
2024-05-28 20:36:05
1244
原创 C语言——循环分支语句
if是最基本的分支语句,它允许根据一个条件来选择性地执行代码块a==1是一个表达式,当其结果为真(及a真的等于1)时,会执行if后面的代码块;否则,会执行else后面的代码块。switch允许根据一个表达式的值选择不同的执行路径。它通过将表达式的值与多个case标签进行比较,从而决定执行哪个代码块。switch语句的基本语法如下:当switch语句的表达式匹配到某个case标签时,程序会执行该case标签对应的代码块,并且继续执行后续的case标签对应的代码块,直到遇到break语句或者。
2024-05-26 11:57:40
335
原创 C语言字符串函数介绍
上图为模拟实现memcpy函数的示例,可以看出memcpy函数中sourc指针将一个字节一个字节的拷贝内存数据到destination指针中,将destination指针与source指针强制转换成char*类型,char*类型的指针每次访问一个字节,这样就能适用于多种类型数据的拷贝。如上图示例 在arr1的字符串中包含了arr2的字符串,那么返回值介绍arr1字符串中首次出现arr2字符的位置,如果没有找到arr2字符则返回空指针NULL.第一个字符串大于第二个字符串,返回大于0的数字。
2024-05-25 18:20:25
898
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人