自定义博客皮肤VIP专享

*博客头图:

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

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

博客底图:

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

栏目图:

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

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

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

原创 二叉树的遍历

二叉树的结构设计:typedef char ElemType;typedef struct BtNode { struct BtNode* left;//左节点 struct BtNode* right;//右节点 ElemType data;//数据域}BtNode,*BinaryTree;二叉树的遍历方式:1.前序遍历访问根节点前序遍历左子树前序遍历右子树void Psoder(BtNode* ptr){ cout<<ptr->data; PsOder(

2020-11-21 21:59:15 748

原创 单链表之首尾相连,成对输出

例子:1->2->3->4->5->6->7输出:1->7->2->6->3->5->4要求:空间复杂度为O(1),即不可借助其他容器思路找到中间的节点将中间以后的节点链接到新的链表头下,反转。然后以头插的形式插入到前半部分的链表中找到一个链表中的中间节点,我们可以使用快慢指针的方式。即指针s走一格,指针p走两格。当p->next为空时,此时的s就处于链表的中间位置申请一个新节点作为头节点,将s以后的链表节点以头插法的形

2021-02-01 22:42:32 1308

原创 单链表k倍反转

例子: 1->2->3->4->5->6->7 k=3输出:3->2->1->6->5->4->7思路:头插法。要用头插法,就需要给链表定义一个虚拟的头节点。找到局部需要逆转的区域,将其局部反转。但是需要注意的是,当第二次进行反转时,我们仍旧需要一个可以代替头节点的节点,所以我们需要记录每个逆置区域快的第一个节点的指针。因为被逆转后,他将成为局部区域的最后一个节点,同时又是下一个局部区域的头节点。还有一点,如果区域的空间不够

2021-02-01 21:57:08 279

原创 单链表的逆置

单链表逆置:题目:给定一个不带头节点的单链表,要求将他逆序输出思路一:定义三个指针tail、p、s。刚开始的时候tail为空,p指向头节点,s指向头节点的下一个节点。第一次进循环,让p指向tail空,此时头节点p已经断开了他的指向然后将p赋值给tail,此时tail也指向的头节点(控制前面的链表节点)。将s赋值给p,s往后走(控制后面的链表节点)。第二次循环,让p指向tail(此时的tail是头节点)也就是让p断开了自己的链接并指向了头节点,将p赋值给tail节点(控制前半部分的链表),然后吧

2021-02-01 21:10:54 3845 3

原创 deepin下linux找不到mysql头文件的解决方法

在deepin下安装好mysql后,发现在c语言中没有<mysql.h>的头文件。出现了这种情况:~/Desktop$ sudo apt-get install libmysqlclient-dev正在读取软件包列表… 完成正在分析软件包的依赖关系树正在读取状态信息… 完成没有可用的软件包 libmysqlclient-dev,但是它被其它的软件包引用了。这可能意味着这个缺失的软件包可能已被废弃,或者只能在其他发布源中找到E: 软件包 libmysqlclient-dev 没有可

2021-01-27 17:37:57 1002

原创 快速排序

快速排序的基本思想:通过一趟排序将一组数据分割成独立的两个部分,其中一部分都是比关键字小的数据,另一部分是比关键字大的数据。然后将这两部分再次排序,最终达到序列有序的目的。从两头开始排序int Partition(vector<int>& arr,int left,int right){ int i=left,int j=right;//让i和j分别指向数组的0号下标和n-1号下标 int tmp=arr[i];将0号下标作为关键值 while(i<j)//当i小于j时

2020-12-09 19:05:11 70

原创 查找排序二叉树的最小值,最大值,Next指针,并逆序打印

给出一个排序二叉树,求它的最小值,最大值,Next域思路:一个排序二叉树,如果要将它从小到大排序,只需要按照中序遍历规则打印即可。那么它的第一个元素就是它的最小值,而最后一个元素就是他的最大值。最小值:BstNode* First(BtNode* ptr){ while(ptr!=NULL&&ptr->left!=NULL) { ptr=ptr->left; } return ptr;}最大值:BstNode* Last(BstNode* ptr){

2020-11-29 11:46:55 940

原创 二叉树的插入与删除

排序规则:定义一个vector数组,将数组中的第一个值作为根节点,此后将后面的值与根节点的值比较,如果小于根节点的值,则放在根节点的左边,如果大于根节点的值,放在根的右边。以此类推,根节点的左子树(右子树)又是一棵二叉树。此外,在二叉树排序中不允许出现两个相同的值。当二叉树完成之后,可以用中序遍历的方式将它从小到达排序。定义结构体BstNode和BSTree分别定义了它们的属性和根节点,这里会增加一个双亲指针,它可以让我们的子节点指向它的父节点,但是这样也会降低它的存储密度。即原来12个字节中可以存储

2020-11-29 11:12:11 5122 1

原创 智能指针

引言:在C++中,堆内存的管理都是由在栈上开辟的变量名间接控制的。这是因为在堆上开辟的空间是没有名字的,我们无法直接对齐进行控制。所以当释放该堆内存时,也需要我们去人为的释放它。智能指针的出现就是为了解决上面的问题,当释放一个堆内存时,它将由系统释放,而不是我们人为的释放它。这样就会避免许多内存泄漏的产生。只能指针总共分为4种:C++98 auto_ptrC++11 unique_ptr shared_ptr weak_ptrauto_ptr特点:1.所有权唯一(即一个堆内存只能有一个指

2020-09-12 20:11:59 3993

原创 TCP协议的概念以及和UDP协议的区别

TCP协议的基本概念TCP的全程是Transmission Control Protocol1.TCP协议为应用程序提供了可靠的、端到端的、面向连接的字节流通信的协议2.利用网络层IP协议提供的不可靠的分组传输服务,用来解决的重传和排序问题字节流:应用程序对数据的发送和接受没有边界限制面向连接:在传送数据之前,明确知道接收方的存在以及传输的一些性质(缓冲区的大小,发送序号的初始值)Unicast:只支持两端点之间的通讯,不支持多播(Multicast)和广播(Broadcast)面向字节流的流逝

2020-08-31 12:11:57 458

原创 http协议以及和https协议的区别

httphttp协议是一种超文本协议,它在应用层通过TCP协议来实现其功能。它的端口号为80号端口当一个浏览器向服务器端请求链接时,首先会进行三次握手。建立链接后浏览器会给服务器发送一个http报文,服务器也会回给浏览器一个http报文。假设客户端在浏览器中输入“www.baidu.com”,通过dns可以找到输入网址(域名)对应的IP地址服务器:从硬件上来讲是一台电脑,从软件上来讲是一个进程(程序)浏览器向服务器发送一个(http)的请求报文,结构如下:方法:请求的资源(网页)+版本号客户端

2020-08-30 16:42:58 284

原创 守护进程

守护进程特点:1.在后台运行,不需要和用户进行交互2.运行周期长,在计算机启动时开启,计算机关机时关闭。在一般的进程中终端关闭时,会话也会随之消失。但是守护进程要在后台运行,而且运行周期长,所以需要设计一个不依赖终端的进程,所以一般会给会话创建一个新的会话,将守护进程放在新会话中。了解守护进程,需要知道这四个概念:1.会话:伴随着终端的打开而产生,随着终端的打开,会和内核建立一个会话。2.会话首进程:终端打开后产生的第一个进程就是会话首进程(一般都是bash进程)3.进程组:创建进程的时候会

2020-08-29 20:48:19 177

原创 TCP的可靠传输之滑动窗口

滑动窗口TCP中的滑动窗口是以字节为单位来发送的。假设现在有两个窗口A和B,其中A窗口向B窗口发送数据,B窗口接收到数据并返回一个确认报文。其中用红色框框出来的字节数就是滑动窗口的大小,滑动窗口的大小取决于B窗口缓存区的大小。滑动窗口越大,说明效率越高。就像拿杯子去接水一样,杯子越大,水管的水流量就越粗。所以说滑动窗口实质上是用来负责流量控制的。前面的1234四个字节是已经发送且对方成功接受的数据,第五个字节是B期望的下一个字节。在滑动窗口中的字节都可以发送出去,但是要保留一会儿等待接受区去接受,如

2020-08-29 09:48:22 515

原创 TCP状态转移图

以下主要讲的是TCP从建立到关闭的整个流程图。SYN:建立链接FIN:关闭连接ACK:响应,确认号PSH:tcp缓存区读取数据ISN:初始化序列号RST:重新建立连接URG:紧急指针是否有效1.服务器端运行,socket套接字处于LISTEN状态2.客户端主动发送SYN,此时变为SYN_SENT状态3.服务器端收到客户端发送的SYN以后,会给客户端发送自己的SYN和ACK,此时变为SYN_RCVD状态4.客户端收到服务器端发送的SYN和ACK,发送ACK进行确认5.服务器收到ACK

2020-08-28 21:05:20 332

原创 继承与多态

继承继承的本质是复用,也就是沿用父类的变量和作用域,并对其做出的扩展。它会使用一个叫做继承列表的符号(:)来将两个类链接起来。例如:class student : public people,就是在student类中沿用并扩展了people类。在习惯上我们将被继承的类叫做基类(父类),继承的类叫做派生类(基类)。派生类(子类)=》基类(父类),派生类继承了基类基类=》派生类,基类派生了派生类继承的方式有三种:public:共有(任意位置)protected:保护(子类类和本类类)priva

2020-08-26 20:10:18 755

原创 通过命令提示符查看c++中类的构造

第一步:在电脑中搜索vs,会出现下面内容(交叉工具命令提示符):输入当前路径:输入dir,找到我们的(继承.cpp):输入cl -d1reportSingleClassLayout(clasname) (XX.cpp) /EHsc,我们在本例中的类是(derve),类名是(继承.cpp)cl -d1reportSingleClassLayoutderve 继承.cpp /EHsc,最终结果如下:...

2020-08-24 12:38:41 382

原创 String类中的写时拷贝函数

拷贝函数拷贝函数分为浅拷贝和深拷贝,浅拷贝是系统自带的一个函数,它的功能是让两个指针指向同一块内存空间来实现内存共享。深拷贝函数是用户根据需求自定义的一个函数,它的功能是让每个对象都有一块独立的内存空间。写时拷贝就是集合了浅拷贝和深拷贝的特性,当我们在调用点只做访问时,用浅拷贝就行。当我们要在调用点修改已存在对象的某个字符时,调用深拷贝函数。设计思想:定义一个计时器,计时器存放在字符串中。每将一个已存在的对象赋值给新对象时,计时器自加;当调用点需要改变某个自负床中的一个字符时,就调用深拷贝来开辟一块新

2020-08-23 12:22:14 725

原创 函数模板和类模板

函数模板函数模板:template <>,<>是模板类型参数列表,<>里面放着类型,它是由typename/class定义而来。但是我们在定义时一般不会用class来定义,因为这会使我们很容易误认为这时一个类模板。它是在编译阶段处理的,也就是说在编译阶段会将代码展开,将typedef T替换成我们需要的类型,所以这其实是一个类型重定义的过程。模板函数与函数模板函数模板是一个模板类型,而模板函数是指一个函数。模板函数是函数模板在调用点实例化后生成的。读起来有点绕口,所

2020-08-23 00:02:39 287

原创 消息队列

消息队列消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。每个数据块中,都包含了一种数据类型。我们在消息队列中获取消息时,可以指定它的类型进行获取。每个消息中都有一个结构体struct mess{};它里面有一个长整型类型,在里面你可以定义自己想要的数据。消息队列中有四种方法:msgget创建消息 msgsnd添加消息 msgrcv获取消息 msgctl修改消息,原型如下:*int msgget(key_t ket,int msgflg);int msgsnd(int msgi

2020-08-21 15:02:38 132

原创 管道及其简单实现

管道管道也叫管道文件,是文件的一种。其中分为有名管道和无名管道,有名管道:可以在任意两个进程间通信;无名管道:只能在父子进程间通信。管道的通信方式是半双工的,那么什么是半双工呢?就像收音机、打印机一样。那既然有半双工,当然也会有全双工,全双工就向电话一样双方都可以进行通话。管道分为读端和写端,顾名思义,写端就是向管道中写入数据,读端就是向管道中读取数据。有名管道首先我们通过mkfifo fifo来创建一个管道,接着我们分别创建出读端和写端。对文件进行进行操作的方法有三种,它们分别是:O_EDONL

2020-08-20 18:56:47 1233

原创 类中默认的函数

类中默认的函数在c++中,我们知道类中默认的函数总共有6个,它们分别是:1.构造函数2.析构函数3.拷贝构造函数4.赋值运算符的重载函数5.取地址操作符的重载函数6.const修饰的取地址操作符的重载函数因为笔者的知识储备量还不够,所以只是简绍一下前面四个默认的函数,希望能对大家有所帮助。首先,什么是默认的函数呢?简单来说就是类中自带的函数,假如我们没有在类中写这些函数,当我们在调用点调用到这些函数的方法时,系统就会拿出自带的函数来进行处理。但是在在有些情况下,当系统提供的函数不能满足我们

2020-08-19 20:43:49 1779

原创 _thiscall调用约定的简单概念

_thiscall我们知道在c中由三种调用约定:_cdecl,_stdcall和_fastcall,其中_stdcall调用约定是windows平台的。在c++中还有一种约定:_thiscall调用约定,它是一种类成员方法调用约定。当我们说起_thiscall调用约定时,我们就不得不说一下this指针的概念了。this指针this指针,就是类成员方法在调用过程中的一个隐藏的参数,当我们实例化一个对象时,如果我们调用类中的类成员方法,我们就会将对象的地址传给this指针,也可以说是this指针指向了一

2020-08-19 10:29:50 774

原创 面向对象语言的思想和封装特性

oop我们知道c语言是一门面向过程的语言,而c++是一门面向对象的语言。那oop思想其实就是一种面向对象语言的思想我们在弄清楚oop语言之前,首先要了解计算机语言设计的目的和意义是什么?通俗的来讲,就是模拟世界。在现实世界中,我们对一个实体因该怎样称呼?我们可以对一只活蹦乱跳的动物叫它兔子,那么这个兔子就是一个实体。那应该怎样取描述这个实体呢?一般描述一个实体,我们会从两个方面去描述:属性和行为。我们可以对它的颜色进行描述,它的毛发是白色的;也可以从它的行为去描述,它会跳,会吃定西。我们也可以把兔子

2020-08-18 20:47:03 100

原创 内联函数

内联函数当我们将sum的调用点写在另外一个cpp中,并在前面加上inline关键字后,我们会发现运行失败。inline int sum(int a, int b)// inline函数将不生成符号{ return a + b;}int sum(int, int);int main(){ std::cout << "sum:" << sum(10,20) << std::endl; return 0; }此时程序运行失败,原因:inline函

2020-08-18 17:45:48 175

原创 信号及其实现

什么是信号信号是UNIX和Linux系统中为响应某些条件而产生的一个事件,通俗的来讲就是给一个进程发送某个信号来做出某些反应。处理信号的方法有三种:SIG_IGN忽略该信号;SIG_DFL默认该信号;还有自定义该信号实现简单的信号系统在定义信号时用了宏定义来处理,它的返回值是一个整形值。在Linux平台上我们可以通过vi /usr/include/bits/signum.h来查看:我们经常用的键盘组合Ctrl+C,就是给某个进程发送了一个SIG_INT信号。可以通过一个简单的程序来实现它:#i

2020-08-18 13:29:36 385 1

原创 僵尸进程

僵尸进程在父进程中fork一个子进程,当子进程执行完成执行exit(0)后,将子进程的退出码存储在与之对应的PCB中。父进程则通过wait来获取退出码,并根据退出码来了解子进程的信息。僵尸进程的概念:当子进程先于父进程结束时,父进程没有获取到子进程的退出码孤儿进程的概念:父进程先于子进程结束,此时将由一个个叫INIT(pis=1,第一个被fork出来的进程) 的进程来“收养”这个孤儿进程。我们将通过一段代码来了解僵尸进程:...

2020-08-17 20:07:03 171

原创 linux关于fork的一些简单概念

我们知道在一个进程中,都会存在一个PCB与之对应,我们一般称它为进程描述符(PCB)。PCB是一个结构体,strcut task_struct,它里面有一个变量名pid,这个变量唯一标识一个进程。那我们在fork一个进程时,实际上就是从内核中申请一个同样大的空间,这个空间的内容大小和被复制的进程是一样。唯一不同的是,指向这两个进程的pid并不是同一个pid。我们习惯称被复制的进程为父进程,复制出来的进程称作子进程需要注意的是,父子进程的返回值是不一样的:父进程返回的是子进程的pid,子进程返回的是0。

2020-08-17 15:07:57 203

原创 new和malloc的区别

在编写代码的时候,我们经常会从堆内存动态的开辟一块空间。但是c语言和c++中,我们动态开辟内存的方法是不一样的。我们知道在c语言中动态开辟用malloc,释放内存块是用free来进行释放。而在c++中我们使用new和delete来进行动态开辟和释放内存。但是它们到底有什么区别,为什么c++不沿用c语言的方式开辟和释放内存呢?首先我们来看malloc和free函数的原型:void malloc(size_t,size);*void free(void ptr);*1.我们知道void是一个半开半闭的区

2020-08-16 19:29:39 232

原创 函数的默认值与重载

函数的默认值函数的默认值是给形参设默认值,如果调用点已经传入实参的话,实参就会覆盖掉形参中的默认值;如果调用点没有传入实参,将会执行形参的值。函数的调用过程中,实参的入栈顺序是自右向左,函数的实参和形参的匹配顺序是自左向右。默认值的赋予规则:1.自左向右依次赋予2.默认值不能重复赋予,否则会出现二义性,编译器将不知道选用谁3.默认值一般会设置在声明上,这样做的好处是如果定义点在调用点的下方,也会调用成功double Perimeter(double a,double b,double c=30

2020-08-15 11:59:11 677

原创 函数的堆栈调用

函数堆栈调用我们先来写一个简单的代码,写一个相加的函数,然后在main函数中调用它,并把结果打印出来。我们知道它最终打印的结果是30,但是main函数(调用方函数)到底是怎么去调用sum函数(被调用方函数)的呢,它的内存布局又是怎么样的,我们可以转到反汇编去看一看它的真面目。#include <stdio.h>int sum(int left, int right){ int tmp = 0; tmp = left + right; return tmp;}int main()

2020-08-14 19:48:58 209

原创 编译链接运行原理

编译链接运行原理我们知道,在操作系统中,只能识别机器码(0,1),为了让一个.c文件编译成为一个可运行的进程,我们需要进行五步操作:预编译,编译,汇编,链接,运行。那它们在这些阶段里做了什么事情,需要我们去研究它们。预编译阶段**预编译阶段我们第一步是删除宏,即删除#define,并将其替换成文本文件。**但是我们一般不会去使用宏,因为它的风险大。看下面代码:我们在宏中定义了一个乘法运算,让ab,编译打印出来的结果没有我们预想的100,而是35。这是因为在宏替换成文本后,它的表达式是这样的:5+55

2020-08-13 12:07:17 331

原创 内存池和通用内存池的简单制作

简单内存池内存池实际上是一种内存的分配模式。它不同于我们平常用的new,malloc来让系统进行内存的分配,它是我们人为的从系统中申请出来一块内存来进行分配。如果我们让系统自动分配则会产生许多内存碎片,这会降低我们的行能,造成内存浪费。内存碎片又分为外内存碎片和内内存碎片,其中外存碎片是指:已经分配出去的内存释放后,该内存不能再重新分配。内碎片是指:开辟过程中,使用点需要的空间小于实际分配的空间所造成的空间浪费。如下图,我们再使用点我们实际需要的大小是4+1,5个字节,但是系统要字节对齐,所以就会

2020-08-12 09:45:28 184

原创 共享库和静态库的制作

库的创建静态库:库是一组预先编译好的函数的集合,这些函数都是按照可重用的原则编写的。它们通常由一组相互关联的函数组成来执行某项常见的任务,标准库的位置一般存放在/lib和/user/lib目录中。库的命名规则名字总是以lib开头以.a或者.so结尾,其中以.a结尾的代表传统的静态函数库,而以.so结尾的代表共享函数库库函数通常以静态库和共享库的形式存在,我们可以用ls /usr/lib命令查看,例子:gcc -o main main.c /usr/lib/libm.a这个命令的含义是让我们编译一个名

2020-08-11 21:15:25 149

原创 用gdb调试单个程序,多线程,多进程

GDB调试程序在linux中使用gdb调试时我们可以在编译的时候加上-g即可。如果你用一个makefile文件做处理,只需要在里面加上调试信息。gdb调试的是正在运行的程序,而不是我们已经编写好的.c文件。如果我们没有添加-g,编译完成后我们可以用ls -l 来查看程序的大小,此时main的大小为5000当我们加上调试信息-g后,我们再用ls -l 来查看main的大小,此时main的大小为6812。这说明在main程序中,已经添加了我们的一些调试信息在调试过程中,我们调试需要进行一些操作,以

2020-08-11 17:58:26 631

原创 cmake和make

cmake和make哈哈哈

2020-08-11 08:37:34 385

空空如也

空空如也

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

TA关注的人

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