自定义博客皮肤VIP专享

*博客头图:

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

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

博客底图:

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

栏目图:

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

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

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

原创 谈一谈网络协议中的传输层

三次握手可以确认双方的接收和发送能力是否正常,可以嫁接同等成本给客户端。第一条本质就是在验证全双工,那么怎么理解第二条呢?

2023-12-11 13:36:16 383

原创 谈一谈网络协议中的应用层

我们在通过网络进行传输数据时,我们要保证,我们在发送时构造的数据,在接收时也能够解析出来,这本质上就是一种协议,是一种应用层协议,我们的程序员可以自定义这种协议,但实际上已经有大佬为我们写出了更靠谱的协议,就是HTTP。其是一种超文本传输协议。那么这种协议到底是什么呢?当我们去访问某个网页时,其先是会将我们的请求通过HTTP请求的方式,发送给服务器,然后服务器再回复给客户端对应的HTTP响应。我们先来看HTTP请求中有什么。其由首行+Header+Body组成。

2023-12-10 12:49:48 667

原创 谈一谈Linux下的进程和线程

新创建出来的子进程,若我们不进行等待,就会形成僵尸进程,僵尸进程会占用一定的资源,我们的子进程在退出时,其会保留一些退出信息,当然由于进程并未完全退出,所以其PCB当然也存在在内核中,所以为了减少资源的浪费,我们要对子进程进行回收,回收可以采用父进程阻塞式的等待,父进程进行轮询判断等方式进行回收,还可以通过信号的方式:因为我们的子进程在退出时,会像父进程发送SIGCHILD信号,所以我们若不关心退出信息,可以通过显式的忽略该信号(特例),关心的话可以在自定义信号处理函数中进行等待(循还+非阻塞式等待)。

2023-12-08 21:32:23 382

原创 谈一谈C++的类对象的存储方式

函数名 的 转换是一样的 , Test 类中的成员函数 Print , 转为 C 语言后 , 方法名变为 类名_成员函数名 , 即 Test_Print;原来的 类名为 Test , 普通成员变量为 mI , C++ 编译器会将类转为 struct 结构体 , 然后将 普通成员变量 转为 结构体中的成员;在C语言中,是不允许有空结构体的存在,所以自然也就没有空结构体大小一说,在C++中,是可以存在空类和空结构体的,(实际上,在c++中,将结构体上升为了类。在C++的类中,有成员变量和成员函数。

2023-12-05 20:42:45 158

原创 谈一谈面向过程与面向对象

在日常生活或编程中,简单的问题可以用面向过程的思路来解决,直接有效,但是当问题的规模变得更大时,用面向过程的思想是远远不够的。面向对象是一种以“对象”为中心的编程思想,把要解决的问题分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个对象在整个解决问题的步骤中的属性和行为。面向过程是一种以事件为中心的编程思想,编程的时候把解决问题的步骤分析出来,然后用函数把这些步骤实现,在一步一步的具体步骤中再按顺序调用函数。易扩展,代码重用率高,可继承,可覆盖,可以设计出低耦合的系统;

2023-12-05 19:58:43 54

原创 谈一谈define,枚举,const,内联

define命令是C语言中的一个宏定义命令,它用来讲一个标识符定义为一个字符串,该标识符被称为宏名,被定义的字符串称为替换文本。该命令有两种格式:一种是简单的宏定义(不带参数的宏定义),另一种是带参数的宏定义。格式:#define 说明:①宏名一般用大写②宏定义末尾不加分好;③可以用#undef命令终止宏定义的作用域④宏定义可以嵌套⑤字符串“”中永远不包含宏⑥宏替换在编译前进行,不分配内存,变量定义分配内存,函数调用在编译后程序运行时进行,并且分配内存。

2023-12-05 18:12:05 88

原创 谈一谈柔性数组

柔性数组是一种动态可变的数组,也许你从来没有听说过这个概念,但是它确实是存在的,是在C99标准底下支持的一种语法。想要使用柔性数组需要满足3个条件:柔性数组只能存在于结构体内,且必须是结构体最后一个成员 柔性数组成员前,至少存在一个其他成员数组的大小未定义int i;int a[0];//柔性数组成员}type_a;int i;int a[];//柔性数组成员}type_a;这两个都是柔性数组。

2023-12-05 17:17:15 126

原创 谈一谈位段

位段是通过结构体来实现的一种以位(bit位)为单位的数据存储结构,它可以把数据以位的形式紧凑的储存,并允许程序员对此结构的位进行操作。由此可以看出,位段是一种节省空间的用法。位段的声明与结构体类似,但有两点不同:位段的成员必须是整型家族的成员(char、int、unsigned int、signed int……)位段的成员后面有一个冒号和一个数字。

2023-12-05 17:01:17 53

原创 谈一谈内存池

内存池就是先向系统要一定量的内存资源,放在我们自己的结构中,自己进行管理,以备不时之需,要是我们每次都直接向系统申请资源,对系统的开销是很大的,所以内存池主要目的就是提高我们的性能。当程序需要申请内存时,从我们事先做好的内存池中直接拿,程序要释放内存时,再放回到我们的内存池中。只有程序完全退出时,再将内存池中的资源归还给系统。内存池可以解决两个主要问题:一,提高效率二,解决内存碎片问题什么是内存碎片呢?内存碎片分为外部碎片和内部碎片。

2023-12-05 11:48:46 363

原创 谈一谈内存对齐

现代计算机中的内存空间都是按字节划分的,理论上似乎我们对任何类型变量的访问都可以在任意地址处进行,但实际上计算机中对基本数据类型的内存中的位置是有限制的,要求其首地址按照某一个数及其倍数进行对齐。有如下例子:4+1=5,本来A的大小应该为5字节,但有了内存对齐后就成了8字节。

2023-12-03 23:35:05 52

原创 谈一谈大小端

大端存储模式:是指数据的地位存储在高地址处,数据的高位存储在低地址处。小端存储模式:是指数据的低位存储在低地址处,数据的高位存储在高地址处。

2023-11-29 23:44:57 69

原创 【1++的Linux】之信号量

任何时候都有一个执行流进入共享资源中,我们将这个共享资源称为临界资源。若我们将这块共享资源当作整体使用就叫做互斥;那么我要是不想当整体使用,而是想要将这块空间继续细分,让不同的执行流访问不同的区域,那么我们不就可以实现并发了吗!但是我们怎么知道有用了多少资源,还剩多少资源。这就用到。

2023-11-27 11:32:29 231

原创 【1++的Linux】之线程(三)含生产者消费者模型

在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从而有效避免饥饿问题,叫做同步。生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。这个阻塞队列就是用来给生产者和消费者解耦的。

2023-11-08 17:51:40 1113

原创 【1++的Linux】之线程(二)

我们先来看其概念:任何时刻,互斥保证有且只有一个执行流进入临界区,访问临界资源,通常对临界资源起保护作用。以上述代码为例,那么我们的临界资源就是。

2023-11-06 22:46:13 1164

原创 【1++的Linux】之线程(一)

1. 什么叫做线程?在官方的定义中:是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。也就是说,线程在进程中运行,是OS调度的基本单位。2. 类比进程对线程再做理解线程在进程中运行,所以一个进程中可能有多个线程,那么要不要将这些线程管理起来???答案是要的,所以,先描述,再组织。用特定的数据结构将线程管理起来,在一般的OS系统中,我们将这种结构称为TCB,但是Linux中却不是

2023-11-04 14:03:32 1200

原创 【1++的Linux】之信号(二)

我们在上一篇文章中讲述了信号的概念和信号的产生,并且我们知道了信号在发送给对应进程后,并不会被进程立即处理,而是进程会在合适的时间去进行处理。那么,在信号未处理的这段时间,信号在哪呢?这就是我们今天所要说的信号的保存。我们先来看一幅图。上述图中就是我们信号在内核中的表示示意图。我们在上一篇中提到过,信号的数量是有限的,并且其本质是一些编号,因此我们可以用位图将其存储起来。我们的pending位图就是用来保存未决信号的。block位图是用来保存阻塞信号的,而handler中则存储的是,我们对应信号的处理方

2023-11-02 19:37:02 1210

原创 【1++的Linux】之信号(一)

就像红绿灯一样,我们要能够处理信号,必须具备能识别信号的能力。那么进程也是一样的,在处理信号前要能够识别相应信号是什么意思。进程怎么能够识别信号呢?------通过程序员。信号的产生是随机的,进程可能在忙 自己的事情,暂时没办法处理信号,那么就会在后续才进行处理,并不是要立刻进行处理。并且进程会临时记录下信号,方便后续的处理。

2023-10-31 20:17:47 1226

原创 【1++的Linux】之进程间通信(共享内存)

我们在前面的文章中提到过,进程间的通信本质都是先看到同一块资源,然后通过这同一块资源进行通信,并且是单向的通信,只能一端发,一端进行读,共享内存也是基于这样的原理而进行的通信,与管道有异曲同工之处,管道是基于文件,拿到同一个文件的文件描述符而进行的通信,需要调用对文件的读写操作函数,因此要经过内核。多个执行流运行时互相干扰,主要是我们不加保护的访问了临界资源(在非临界区是没有影响的),为了更好的进行临界区的保护,我们让多执行流在任何时刻都只有一个进程能够进入临界区----我们将其称为互斥。

2023-10-27 19:46:03 1156

原创 【1++的Linux】之进程间通信

我们在创建子进程之前分别以读写方式打开同一个文件,子进程继承之后,其也能够这个文件的文件描述符,有了文件描述符,我们是不是就能够访问这个文件了!我们让父进程进行写,那么就关闭其读的那个文件描述符,让子进程读,那么就关闭其写的那个文件描述符。命名管道与匿名管道的原理相同,都是通过让两个进程看到同一份资源,从而实现通信,但命名管道不再局限于父子进程之间,而是任意两个进程之间实现通信。命名管道的本质也是一种文件,但不是普通的文件,普通的文件我们在读写时,会将内存数据刷新到磁盘中,但是我们的管道是不会的。

2023-10-25 19:14:11 1448 1

原创 【1++的Linux】之文件(三)

也就是我们的当前目录。内最多只会存放一个文件的数据(即不会出现两个文件的数据被放入同一个 Block 的情况),如果文件大小超过了一个 Block 的 size,则会占用多个 Block 来存放文件,如果文件小于一个 Block 的 size,则这个 Block 剩余的空间就浪费掉了。根据inode找到对应的区组,找到对应的inode,获得其所占用的数据块的编号,再找到blockbitmap,将其对应的位置为0即可,再找到inodebitmap,将其inode对应的位置为0 ,并没有删除其实际的数据内容,

2023-10-23 12:45:44 1126

原创 【1++的Linux】之文件(二)

文件的写入读取都要靠文件描述符去找到所对应的文件,既然本应该向屏幕中写入的内容,写到了其他的文件中,那么一定与文件描述符有关,我们前面说过,文件描述符是数组的下表,这个数组存储的是文件对象的指针,那么就好理解了,发生上述现象的原因,就是该下标中的内容发生了改变,使得原本指向屏幕文件,被替换为了Log.txt这个文件对象的指针。这样的模式称为写回模式。在OS的软件设计层面,我们将文件的成员属性和方法都放在结构体中,其方法我们使用文件指针的形式,统一了方法的接口,但是不同的对象去调用,会有不同的结果。

2023-10-13 11:32:12 1110

原创 【1++的Linux】之文件(一)

我们的fopen,fread,fwrite实际上都是调用了系统的这些接口,但是是经过了一些封装的,为了方便我们用户使用。------都是通过封装的文件相关的函数就行操作,那为什么不知直接用系统相关的调用函数呢?站在系统的角度,能够被写入,或是被读取的 设备就叫文件。广义来说:我们的显示器,键盘,网卡等都是文件。因此对文件的操作无非就是对文件内容的操作和对文件属性的操作。其中flags代表的是打开的方式,mode代表若文件不存在,新创建文件的权限。我们发现其新建的文件也在次路径下,这也就证明了上述的结论。

2023-10-07 17:43:31 1179 3

原创 【1++的刷题系列】之双指针

常见的双指针有两种形式:一种是对撞指针(也称为左右指针);另一种是快慢指针。对撞指针从两指针向中间移动,一个指针在最左端,一个在最右端,逐渐向中间移动。其终止条件一般是两指针相遇或错开,或是得到结果,直接跳出内部循环。快慢指针的基本原理就是两个速度不同的指针在数组或链表上移动。这种方法处理环形链表或数组是非常有用的。不单单是环形链表或数组,处理循环往复的情况时我们都可以用快慢指针来解决。最常用的一种快慢指针就是快指针走两步,慢指针走一步。

2023-10-04 20:57:47 1106 9

原创 【1++的Linux】之进程(五)

我们创建出来进程是要其做事情的,它可以去调用函数,或者是执行其他的程序,子进程通过exec函数族执行其他的程序就叫做进程替换。也就是在调用进程内部执行一个可执行文件。当进程调用一种exec函数时,该进程的代码和数据完全被新程序替换,新程序从main函数开始执行,由于未创建新进程,所以替换前后进程的id等并不改变。在加载新程序之前,父子进程的关系是:代码共享,数据写时拷贝。当子进程加载新程序的时候就是一种“写入”,此时代码也就需要进行写时拷贝,进行分离!!!

2023-10-02 16:53:17 1398 1

原创 【1++的Linux】之进程(四)

代码和数据一般是从磁盘中来的,内核结构就是我们一直说的所谓的pcb,为了描述和控制进程模块,系统为每一个进程都定义了一个数据结构—也就是我们的task_struct。由于我们的printf没有带换行,所以没办法自己冲刷缓冲,通过结果我们看到,当用exit时,其能够输出,用_exit时,没有输出,则证明,exit具有冲刷缓冲的作用而_exit没有。是因为若不休眠,则我们的父进程就可能先执行完退出了,那么子进程就成了孤儿进程,它的父进程就成了bash。新创建的进程叫做子进程,原来的进程叫做父进程。

2023-09-29 14:52:52 1242

原创 【1++的Linux】之进程(三)

进程地址空间的分布。其分为了内核空间和用户空间。从具体进程的角度来看,每个进程可以拥有4G字节的虚拟地址空间(也叫虚拟内存)。其中每个进程有各自的私有用户空间(0~3G),这个空间对系统中的其他进程是不可见的。最高的1GB内核空间则为所有进程以及内核所共享。而用户空间又被细分为我们所熟知的:堆区,栈区,代码区…

2023-09-24 18:00:46 2166

原创 【1++的Linux】之进程(二)

1. R---运行状态:并不意味着进程一定在运行中,也可能在运行队列中。2. S--睡眠状态:意味着进程在等待事件完成。(可中断睡眠)3. D--磁盘休眠状态:在这个状态的进程通常会等待io的结束(不可中断睡眠)4. T---停止状态:可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。5. X---死亡状态:这个状态只是一个返回状态,你不会在任务列表里看到这个状态。

2023-09-23 19:50:10 2018

原创 【1++的C++进阶】之特殊类设计

让对象只能在堆上创建,那么我们应该将构造函数,拷贝构造都私有化,使得用户不能自己在栈上创建对象。我们再提供一个静态函数的接口,其返回的是一个指向堆上对象的指针。与上述一样,我们将构造函数私有化,使得对象不能够直接在类外创建在栈上的对象。我们将其构造,拷贝与赋值私有化,防止其在类外创建对象,并且在类中声明了一个自己的静态对象,再提供一个访问它的全局访问点。该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。单例模式的实现有两种:饿汉模式和懒汉模式。

2023-09-20 10:14:01 2008

原创 【1++的C++进阶】之智能指针

要了解智能指针,我们先要了解RAII.RAII是一种利用对象生命周期来控制资源的技术。在对象初始化时,其接管资源,在对象的生命周期内其管理的资源始终保持有效,最后当对象析构时,释放资源。那么什么是智能指针呢?智能指针就是利用了RALL的原理,并且通过封装,使得它的对象能够向指针一样使用。因此其重载了*,->。new 与new[] ,delete与delete[]有什么区别呢?

2023-09-18 20:53:00 2036

原创 【1++的C++进阶】之异常

程序的错误大致可以分为三类:分别是语法错误;逻辑错误以及运行时错误。语法错误在编译链接阶段就能够被发现,只有100%符合代码规则的语法才能够 被编译通过,生成可执行程序。逻辑错误是指我们编写代码的思路发生问题,达不到预期目标,对于这种问题我们要进行调试解决。运行时错误是在程序运行期间发生的错误,如数组越界,除0错误,内存申请失败等,我们今天要学习的异常就是为了解决这种问题而引入的。assert断言,直接终止程序。当由于某些小问题就使得程序终止,影响用户体验。

2023-09-17 13:33:54 1968

原创 【1++的C++进阶】之emplace详解

通过运行结果我们可以发现,insert在插入时,由于我们传的是右值,其调用了构造和移动构造,而emplace只调用了构造函数。也就是说,emplace是在插入位置直接构造元素,而不是和insert一样,先是构造好,再移动或复制到插入位置。

2023-09-15 14:59:34 2582

原创 【1++的C++进阶】之C++11(二)

假设我们现在需要对一个集合进行排序,(我们用std::sort进行排序)当我们要排升序时则需要传一个升序规则的仿函数,要降序时,则传一个降序规则的仿函数,当要元素类型不同时,则又需要该这个仿函数。因此实际在底层编译器对于lambda表达式的处理方式,完全就是按照函数对象的方式处理的,即:如果定义了一个lambda表达式,编译器会自动生成一个类,在该类中重载了operator()。通过上述结果我们可以发现,对于自定义成员,其在没有自己实现析构函数,拷贝构造,赋值重载时,会自动调用自定义成员的移动构造。

2023-09-13 21:42:17 1815

原创 【1++的C++进阶】之C++11(一)

C++11带来了哪些变化?相比于C++98/03,C++11则带来了数量可观的变化,其中包含了约140个新特性,以及对C++03标准中约600个缺陷的修正,这使得C++11更像是从C++98/03中孕育出的一种新语言。相比较而言,C++11能更好地用于系统开发和库开发、语法更加泛华和简单化、更加稳定和安全,不仅功能更强大,而且能提升程序员的开发效率,公司实际项目开发中也用得比较多。

2023-09-12 21:57:22 997

原创 【1++的数据结构】之哈希(二)

位图就是用每一位来存储一种状态。位是表示信息的最小单位,每一位存储一种状态,那么我们可以将每一位的状态与数据映射。那么便可以用较少的内存去处理海量的数据。通常用其判断某个数据存不存在。

2023-09-11 15:38:59 1086 1

原创 【1++的数据结构】之哈希(一)

首先我们要知道的是哈希是一种思想----一 一映射。在以前我们讲过的容器中,查找效率最高的就是二叉平衡搜索树,由于其关键码与存储位置之间没有对应的关系,而是通过多次比较关键码的大小来查找,查找的效率取决于比较次数,查找的时间复杂度可以达到O(logN)。最理想的查找便是不经过任何的比较,直接能够锁定查找值的位置,因此,如果能够构建一种结构,通过某种函数能够使得关键码与存储位置之间一一映射,便能够快速的找到要查找的值。其实,有点类似与通信中的调制与解调哈。

2023-09-07 20:14:49 2651 2

原创 【1++的数据结构】之map与set(二)

详解了红黑树的底层结构与实现,并对map与set进行了封装

2023-09-04 17:51:16 2385 1

原创 【1++的数据结构】之AVL树

在说AVL树之前我们先来说说为什么会出现AVL树。在前面的文章中我们讲过二叉搜索树,虽然查找,插入效率比较高,但其有个缺陷:在某些情况下其可能会成为一颗单支树或其他高度较高的树,这时我们的效率就比较低了,甚至接近于O(n)。在此背景下,有两位俄罗斯数学家,便发明了AVL树----当插入新的结点后,保证每个结点的左右子树的高度差不大于1。则这颗树就会接近满二叉树,其高度就会降低,那么查找,插入效率也会提高。

2023-08-29 22:42:25 2541

原创 【1++的数据结构】之map与set(一)

用来表示具有一 一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。STL总共实现了两种不同结构的管理式容器:树型结构与哈希结构。树型结构的关联式容器主要有四种:map、set、multimap、multiset。这四种容器的共同点是:使用平衡搜索树(即红黑树)作为其底层结果,容器中的元素是一个有序的序列。

2023-08-23 19:32:11 2458

原创 【1++的数据结构】之二叉搜索树

二叉搜索书又叫二叉排序树或二叉查找树,其左子数上的节点小于根节点;右子树上的节点大于根节点。如图,就是一颗二叉搜索树。

2023-08-07 12:21:06 2174

原创 【1++的C++进阶】之多态

多态的定义:不同继承关系的类对象,去调用同一个函数,产生不同的行为。再说通俗点就是:一个行为,不同的对象去做会产生不同的结果。被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写是虚函数 (被virtual修饰的成员函数)三同(函数名,参数,返回值)子类虚函数不加virtual依旧构成重写。重写的协变:返回值可以不同,但必须是父子关系的指针或引用如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加virtual关键字,都与基类的析构函数构成重写。

2023-08-04 15:07:38 1936

空空如也

空空如也

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

TA关注的人

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