自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(47)
  • 收藏
  • 关注

原创 C++: 二叉搜索树及实现

由于使用递归,所以需要取root的引用,参数就要给root,而在类外是没有办法获取作为私有的root的,因此只能把实现的细节写在private里。

2024-05-27 22:00:59 379

原创 C++: 多态

在Func调用基类的对象a1时,Func从传来的引用,基类的对象a1中去找虚函数指针,找虚表,然后找到要调用的函数。如果是派生类的对象b1,就从b1中去找虚函数指针,找虚表,找到相应的地址和指向的函数,从而实现了多态,即相同的接口,传的对象不同,实现不同的行为。

2024-05-23 18:46:32 834

原创 C++: 继承

C++的语法复杂,因为多继承和菱形继承复杂,还有了虚继承,虚基表,表中存偏移量。这些就一直在填多继承语法的坑。

2024-05-21 20:37:10 861

原创 C++: 优先级队列的模拟实现和deque

1.11 优先级队列是一种容器适配器,根据严格的弱排序,它的第一个元素总是它所有元素中最大 的。适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结),这种设计模式将一个类的接口转换成客户希望的另一个接口。比方说,在stack里面,把push接口转换成调用另一个容器的push_back,这个容器的类放在私有里,是根据初始化来定的,你希望这个底层容器是vector,那么在调用stack的push时,实际调用的就是vector的push_back进行插入元素操作。

2024-05-19 14:19:34 888

原创 C++: stack类和queue类的使用及模拟实现

1.11 stack是一种容器适配器,专门用在具有先进后出的上下文环境中,插入和删除都只能从一端操作。1.12 stack作为容器适配器实现,它底层是其他类或容器。它只需要提供一些函数接口实现功能就好。1.13stack的底层容器可以是任何标准的容器类也可以是其他特定的容器类。这些容器应该支持以下操作:empty:判空push:压一个元素入栈top:取栈顶的元素pop:在栈顶出数据。

2024-05-17 21:10:44 454

原创 C++ : list类及其模拟实现

1.list 是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。2.list的底层是带头双向可循环链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。3.list和forward_list非常相似:最主要的不同在于forward_list 是单链表,只能朝前迭代,让其更简单高效。4.与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。

2024-05-05 22:48:04 1041

原创 c++: vector和其模拟实现

vector是顺序表,在数据结构学习顺序表时,插入的数据是int,但实际上顺序表作为一个数组,里面的元素可以不止是整型数组,可以是char等其他类型的。这一点是有必要转变的。vector运用动态内存分配空间,比起其他容器,vector在插入和读取数据上比较方便,但是在删除时要一个个挪动数据,这一点可能会有点效率低,因此需要频繁删除的文件不适合用vector存储。vector在使用中需要掌握主要的几个接口,其他情况看文档和c++官网的说明就可以使用。本次模拟实现只是为了更深入了解vector的功能。

2024-05-01 15:36:28 357

原创 C++: string类的模拟实现

本次模拟实现主要是为了更深入地了解string类的各种接口和函数的功能。所以并不是写一个能和库媲美的模拟实现,只是实现其中主要和常用的功能。

2024-04-27 15:34:20 857

原创 C++ :string类模板的使用

学完模板以后,基本都知道模板分为函数模板和类模板。它将底层逻辑封装起来,向外提供接口,当使用的时候,只要调用一份模板实例化对象就可以,不需要自己写。这大大提升了写代码的便利。C语言中,字符串是以'\0'结尾的一些字符的集合。为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数和字符串是分离开的,底层空间要用户自己管理,不注意还会越界访问。所以要学习string类。在使用string类时,必须包含#include头文件和using namespace std;

2024-04-19 19:35:07 995

原创 C++: 模板初阶

当给函数传参的时候,传的参数不一样,编译器会根据参数进行推演函数的参数类型应该是什么样,然后编译器自己写一份这样的函数给用户调用,所以并不是没有这份代码了,它就不存在了。这种情况,如果模板函数和调用时更匹配,编译器会用模板函数实例化,而不会用非模板函数的类型转换。在汇编的时候,或者把函数的地址打印出来,可以看到不同参数调用同一份模板会产生不同的地址的。如果显式实例化类型不匹配,编译器会尝试使用隐式实例化,如果转换失败,那么编译器会报错。函数模板只是一个模具,它只有在调用的时候,才会实例化。

2024-04-15 20:18:09 337

原创 C++: 内存管理

调用operator new 申请空间在申请的空间上执行构造函数,完成对象的构造。

2024-04-14 14:09:07 546

原创 C++: 类和对象(下)

最后一个计算日期之间相减的方法,我用了日期的前置++,调用了前置++的方法,前置++里面,又调用了+=的运算符重载,在+=这个方法里,我写了日期相加天数以后,超过本月天数和超过12个月的预防措施。这么写之后,该函数就是该类的朋友,函数可以使用类的成员(包括对象和函数),但是朋友是单向的,不是双向的。1.初始化列表的初始化顺序,是按照下面成员变量声明的顺序进行的,不是按照列表写在前后的顺序。则Time类的成员Date都可以访问,但Date类的成员,Time不能随意访问。在Time类中,写了Date类的声明。

2024-04-12 20:06:51 590

原创 C++ : 类和对象(上)

2.用户没有显式定义时,编译器会生成一个默认赋值运算符重载,以值的方式拷贝。对内置类型是直接赋值,对自定义类型调用该自定义类型的赋值运算符重载来完成赋值。3.像拷贝构造一样,赋值运算符重载也面临着对申请过资源的情况,必须自己写一个赋值运算符重载,而不能依赖系统给的。否则可能会发生两个不同对象占用同一块空间的尴尬局面。并且一个空间的销毁也会影响另一个空间。前置++和后置++重载。

2024-04-05 21:58:47 678

原创 C++: auto/范围for/类

1.声明和定义全部放在类体中。成员函数如果在类体中定义,编译器可能会把它当作内联函数。前面说了,内联函数声明和定义是不能分离的,因为内联函数会直接展开,如果分离是找不到地址的。2.类声明放在.h文件中,成员函数的定义放在.cpp文件中,注意,在定义成员函数时,成员函数的名字前面和返回类型的后面要加。

2024-04-04 22:55:27 723

原创 C++: 命名空间/C++输入输出/缺省参数/函数重载/引用/内联函数

2.一个变量可以有多个引用3.引用一旦引用一个实体,再不能引用其他实体。

2024-04-01 22:23:30 1039

原创 数据结构:排序

后面的数再和已经有序的数组中最后一个比,比它小再往前依次比,直到找到比手中的数更小的数,就放它后面了。最后再把有序的新数组的数复制到之前的数组。数组第一个数和第二个数比,大的往后,然后第二和第三比,大的往后,走一趟确定一个最大的数,然后排剩下的,把剩下的中最大的排在倒数第二。以此类推,当这个分母越大,逼近于N时,间隔就越小,间隔趋近于1,整体越发有序,当gap=1时,就是有序排序了。关于这个排序稳定性的说明,它取数是依次取的,就算值相等,那么先取的在前面,后取的在后面,根本不会改变相同值的位次。

2024-03-31 23:15:41 714

原创 数据结构:堆和二叉树遍历

堆顶就是这个堆最小的数,堆顶和这个堆的最后一个数换位置,然后再把最后一个数取出,再pop这个数。答:先取出50个建小堆,剩下的数和堆顶相比,遇到大于堆顶的数就直接替换掉堆顶的数。替换一次,小堆也要向下调整一次,保持它是一个小堆。向下调整就是每次取个数,由于和堆的最后一个数交换了位置,取出之后的二叉树需要调整一下才能成为一个堆。如果是大堆,就比较堆顶的和左右子树,大于它,堆顶和大的那个交换,这样层层交换下去。此问题也可以用于:内存空间不够,建堆数量有限,如何在大量的数据中取出前k个最大(小)的数。

2024-03-20 23:19:55 656

原创 数据结构:树和二叉树

也就是说,二叉树的所有节点的孩子,最多只能有两个。如上图所示,F节点是所有节点的根节点,也是祖先节点。2.若 2i+1 < n , 左孩子序号: 2i+1, 2i+1>=n 则无右孩子。3.对任何一棵二叉树,如果度为0的叶子节点数是N0,度为2的节点数为N2,则有N0=N2+1。1.如果根节点是第1层,那么一颗非空二叉树的第i层上最多有2^(i-1)个节点。4.若规定根节点是第一层,具有n个节点的满二叉树的深度为h=log(N+1).2.如果根节点是第1层,那么深度为h为的二叉树最大节点数是2^h-1。

2024-03-12 15:55:15 421

原创 数据结构:栈和队列

队列:一种特殊的线性表,只能从固定的一端入数据,从另一端出数据。一般把出数据的一端叫队头,入数据的一端叫队尾。队列要频繁的头删尾插,如果用顺序表,就要在删除时,不断地挪动后面的数。所以一般用链表实现。栈:一种特殊的线性表,只能从固定的一端入数据和出数据。一般把出入数据的一端叫栈顶,另一端叫栈底。栈的出入顺序遵从——后入先出的原则。下面是stack.c文件,具体实现栈的各个功能。压栈:把数据放入栈中,叫压栈/入栈/进栈。上图压栈按照1234的顺序连续放,出的时候是4321。出栈:把数据移除栈中,叫出栈。

2024-03-06 20:13:30 525

原创 【原创】烟花实现,基于windows操作系统

我尝试了一下,在9-15是最合适的,选择了12个结点。我首先把c语言本地化,然后复制了许多特殊字符,尝试它们在控制台能打印出来的结果,把能够打印出来的筛选字符都列出来。花筒直接用宽字符打印出来就可以,烟花的花束需要设置一个结构体,和贪吃蛇的蛇身一样(贪吃蛇的实现见上篇文章),只不过是单向向上移动,而且移动的速度要快许多。烟花的实现是我自己独立实现的第一个项目。借助贪吃蛇的删尾的思路,在原坐标处打印两个空格字符就可以覆盖掉。下面是花筒,上面是烟花在上升时的花束和升到制高点时绽放的花体。(虽然是长方形的烟花)

2024-02-12 22:45:59 524

原创 贪吃蛇的实现,基于windows操作系统

收尾部分主要是游戏玩了一把game over以后,因为各种原因结束而打印不同信息。打印完了要把蛇的结点依次释放。然后再把食物释放,把传来的维护蛇的指针置为空。如果想设置再来一把的消息,可以在test.c文件里进行。要注意两个getchar。第一个getchar用来读取信息,通过一个变量来接收,用于判断玩家到底要不要开下一把。第二个getchar用来接收读取回车字符,但是没有变量接收它,也就是它不产生实际作用。因为它的目的只是用来吸收回车,使这个回车键不至于影响到下一次的输入判定。

2024-02-12 22:20:48 1059

原创 数据结构:用顺序表和单链表实现通讯录(下)

如果测试文件中不需要调用,但在函数实现中需要用到,可以写在函数定义的slcontact.c文件(亦即可以不声明)。如下图所示,通讯录是一个结构体,里面存有数据和下一个结点的地址。删除联系人时,需要查找联系人(也可以封装一个函数),找到以后再删除,删除完了再展示一下当前通讯录(这一点可不写)如果要改变当前指针的地址,就用二级指针接收,如果不需要改变,就可以用一级指针接收。需要注意,顺序表是直接创建的结构体,这里是创建一个结构体指针。添加联系人时,需要一个创建新结点的函数以及一个尾插的函数。

2024-02-01 17:42:16 484

原创 数据结构:用顺序表和单链表实现通讯录(上)

首先简要介绍顺序表和链表的概念和区别以作区分。顺序表:逻辑上是线性的,物理性质上也是线性的。逻辑是线性的(连续的)体现在它可以通过第一个数找到接下来的数。物理性质上的线性体现在分配给它的内存是连续的。它本质上就像一个数组,可以通过下标来访问成员。单链表:这里说的单链表是指不带头单向不循环链表。链表和顺序表是不同的。链表在逻辑上是线性的,但在物理性质上是非线性的。需要的时候申请一块内存,但这块内存和其他内存不一定连续,它可能是散落在四周的,只是通过记住下一个节点的位置,使这些单独存在的空间有了联系。

2024-01-31 17:57:49 556

原创 C语言:编译和链接

我们写的代码实际上是一个文本文件,对文本文件运行不会生成可执行程序。但是在VS或者gcc等开发环境中,内置了编译器和链接器,使它生成一个计算机可以理解可以执行的二进制程序,这个可执行程序用.exe为后缀。编译和链接主要就是解释代码是如何从文本文件变成可执行文件的。编译这个过程就是把一个工程中的各源文件,通过编译器,编译成.o为后缀的目标文件。链接就是把这一个个的.o为后缀的目标文件整合处理成一个.exe为后缀的可执行程序。需要注意,.o为后缀的文件也是计算机可以理解的二进制文件。

2024-01-19 17:53:21 920

原创 C语言文件操作

文件可以保存数据,如果没有文件,我们写的程序的数据保存在电脑内存中,程序结束后,向内存申请的空间就会被操作系统回收,数据就没啦。流是一个抽象的名词,用于对各种设备的统称。每个文件都在内存中开辟了一个相应的文件信息区,用来存放文件的信息。程序文件包括源程序文件(.c后缀),目标文件(windows环境后缀是.obj),可执行程序文件(后缀.exe)。比如说10000,用ASCII形式,就要占5个字节,用二进制形式,占用4个字节。每当打开一个文件,系统会自动创建一个FILE类型的结构体变量,用来操作这个文件。

2023-12-15 15:52:53 471

原创 C语言动态内存管理malloc/calloc/realloc/柔性数组

介绍三个库函数,它们可以直接向内存申请特定大小的空间,然后就可以使用这些空间了。这三个库函数分别是malloc calloc realloc明明已经有结构体、数组、int、float、double等类型可以直接创建,向内存申请空间。为什么还要这三个库函数呢?前者申请创建的内存大小是不能改变的,创建时是多少就是多少。而malloc、calloc和realloc申请的内存空间是可以改变的。如果感觉内存不合适,随时可以再加或者减。前者创建的内存是在栈区,而通过malloc等函数申请的空间在堆区。

2023-12-09 14:34:11 1101

原创 C语言联合和枚举

枚举的每一个成员都有默认值,第一到最后一个依次从0递增。如果中间某个成员定义为388(或某个数),后面的也依次递增(389...不再按照默认值)。枚举有类型检查,有作用域,一次可以定义多个常量,便于调试,增加代码可读性。它也是由多个不同类型的成员构成。不同的是,联合体的成员共用一块空间。枚举可以定义常量,枚举里面可以包含可能的全部取值。枚举中,定义完1个常量后,用逗号。联合体的大小至少是最大成员的大小。是成员中最大对齐数的整数倍。联合体不能同时取所有成员的值,而只能取其中一个。改变b的值也会改变a的值。

2023-12-06 19:26:09 391

原创 C语言结构体和位段

2.编译器在读取内存的时候有些是8个字节8个字节的取,如果不采用对齐数,就可能出现一次读取没读到完整的字节,要读取好几次才能读取到一个变量。不一样的是,stu是自己定义的标签。4.结构体有嵌套的情况下,嵌套的结构体成员对齐到自己成员中最大对齐数的整数倍上,结构体总大小是内部所有成员中最大对齐数的整数倍。1.有些编译器只能从特定的对齐数上取数,比如int型从4的对齐数取数,如果不采用对齐,那么取不到数。位段的内存申请不是按照比特申请的,是按照char和int的方式,也就是1个字节或者4个字节来申请的。

2023-12-05 18:47:42 1259 1

原创 C语言数据在内存中的存储

也可能是 44 33 22 11的顺序存储,地址从左到右,由低到高,44是低位,存在低位地址,说明该模式是小端存储。如果留下的是00,这显然是高位的数,低地址存高位内容,就是大端。留下的是01,也就是1,这是低位的数,存在低地址,所以是小端。使用反码存储,是因为计算机可以把符号位和数值位进行统一处理,加法和减法也可以统一处理,补码和原码之间的换算也是相同的,不需要额外的硬件电路,非常方便。,因为只能存1个字节,就是8个比特位的数,就算把数塞满,最多也就是127,如果符号位是1的话,那就是负数。

2023-12-04 16:58:44 518

原创 C语言内存函数

这里主要介绍两个内存函数:memcpy和memmove这两个函数都是库函数。

2023-12-03 13:03:24 556 1

原创 C语言字符函数和字符串函数

当*m==*n时,flag是不变的,flag储存的是他们相等的第一个字符的位置。例如:目标字符是swimming,源字符是min,strstr会从swimming的第一个字符s开始找,找它是否和源字符的第一个字符m相等,相等就下一个对比。作用是把一串字符复制到另一串字符中,用源字符取代目标字符,源字符会替代目标字符的 末尾的 \0。相同作用的还有strncat,多了一个参数num,将源函数追加到目标字符后面,会追加\0,如果源字符小于num,那也不会多加内容。在C语言中,0是假,非0是真。

2023-11-30 20:45:14 962

原创 C语言回调函数/qsort

当一个函数的地址/指针被当作参数,传给另一个函数,这个指针被用来调用它所指向的函数时,这个被调用的函数就是回调函数。回调函数不是函数实现方调用,而是特定场景由另一方调用,用于对该事件的回应。return x+y;int x = 0;int y = 0;int i = 0;int j = 0;return 0;把函数Add作为函数Operation的参数,定义函数Add,定义函数Operation。在Operation中,传的是函数,所以形参用函数指针p变量接收Add的地址。

2023-11-26 14:44:25 371 1

原创 C语言二维数组传参/函数指针变量/函数指针数组/转移表

(*(p+0)+0)访问arr[0][0],*(p+0)表示第一行的地址解引用,解引用后就是二维数组的第一个元素arr[0],arr[0]既是二维数组的第一个元素,也是第一行数组的数组名,作为数组名,它又是首元素的地址。二维数组传参传的是首元素的地址,在这个例子中,首元素的地址----arr[0]----的地址,arr[0]是一个一维数组,它存入的是arr[0]中首元素----arr[0][0]---的地址。以上是函数指针数组的声明,p[3]结合表明这是个数组, int * () 表示是一个函数指针。

2023-11-25 20:58:24 1200

原创 C语言指针访问数组/一维数组传参/指针数组

指针变量p里面存的是数组arr的地址。数组的地址通常指向首元素的地址,但是p++的话,就会跳跃一个数组,指向arr数组后面的地址了。*m指示m是一个指针变量,int *表示m所指向的是一个整型指针。上面演示就是把数组的首元素地址放在指针变量 p 里面,然后通过对 p的操作、解引用,来打印整个数组。//p是一级的整型指针变量,储存的是整型变量a的地址。以上是数组p和[5]结合,int*表示数组里面元素的类型是int型指针。一维数组传参的本质传的是地址,而且是首元素的地址,而不是整个数组。

2023-11-23 23:05:11 364

原创 C语言初步理解指针

p = 3表示 对指针变量p解引用,意思是通过对p储存的地址找到该地址对应的内容,在这里储存的是a的地址,所以*p就是a。变量a 储存了10,*p = &a表示把a的地址取出来放到指针变量p中,*p 前面的 int 表示指针变量p所指向的内容 ----a-----是一个int型的变量。为了避免野指针的使用,在初始化时,知道指针指向哪里,就取地址,不知道的话,就给指针赋值NULL;const放在*后,就是不能通过改变p的地址来改变所储存的内容,但可以通过改变*p来改变纯粹的arr[0]的值。

2023-11-22 17:47:28 29

原创 C语言函数递归

迭代有很多,循环只是其中一类。如for循环、while循环、do while循环等都属于迭代。函数不断调用自己,就可以层层求出值了。使用方式,通常是把复杂的式子拆解成构成它最基本的式子并有一定规律性。如:求n的阶乘,可以把它写成两个式子,当n=1时,和n大于1时。递归的优点是式子简单,列出来就能算,缺点也很明显。

2023-11-22 17:09:35 31

原创 for循环

跳出循环后,i++,此时 i 符合外层for循环条件,j 符合内层 for 循环条件。无论之前 j 的值是什么,此时 j 的值都会重新被更新为0。(因为 j 是局部变量,跳出循环后就会被销毁,值不会保留,重新进入循环后,再重新生成,所以是0)三、p 也是局部变量(在主函数内部),不过 p 的范围比两个 for 循环更大,在括号外。所以,即使内部有 break 跳出了 for 循环,p 的值仍然会保留,连续。循环中是可以随后面的 i++,以及内部的对 i 的操作保留值,它是连续变动的。

2023-11-14 20:35:46 42 1

原创 C语言 位操作符/结构体

< 左移操作符 也用于对二进制数的操作,对二进制数往左移一位,往右边补0。>> 右移操作符 用于二进制数的操作,对二进制数往右移一位,往左边最高位补符号数,正数补0,负数补1。负数的最高位是符号位,为1,原码取反得到反码,反码+1就是补码。其中需要在计算中转化。~ (单目操作符)按位取反 对一个二进制数,1的位取0,0的位取1。| 按位或 两个二进制数比,同位有1就是1,其他都是0。& 按位与, 两个二进制数对比,同位为1才为1,其他都为0。正数的原码,反码,补码都是一样的。

2023-11-14 20:16:59 43 1

原创 扫雷游戏(9*9)

我想设计一个Print函数,它一开始就要打印。让我选择游戏开始还是退出。选择1,游戏开始,选择0,游戏退出。退出以后不再循环,游戏结束。选择1,则游戏继续。因此,这里采用do while循环,条件写上 i 的值。和switch循环配套。选择0以后,退出游戏,但我想要它再显示一下进入游戏的界面,所以它要打印一下再退出。选择1,则开始游戏,写一个game函数,我需要game函数帮我实现玩游戏的功能。其他选择就是选择错误,就再输入一遍。到这里我的主函数的内容就写完了。

2023-11-10 22:14:11 264 1

原创 C语言:自定义函数/数组传参

定义函数的方式。(返回类型)函数名(参数)函数的定义是一种特殊的声明,所以定义函数以后,在下面就可以直接调用了。调用函数只需要写函数名(参数)就好。1.可以使整个程序的主函数部分代码不那么长,逻辑清晰。2.可以分离程序,比如把函数的定义放在另一个源文件作为全局变量。那么只需要在本文件声明一下就可以。分离程序方便隐藏程序。定义函数的目的,主要是需要实现一个功能。如果直接写在主函数的程序里写的内容过长,括号太多,逻辑不那么清晰。

2023-11-07 13:28:03 659

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除