C++
文章平均质量分 86
C++
_Zebra
即使可能性很小,也要一直努力,无限接近于0的可能性永远不会是0
展开
-
*(void**)解析——如何设计可以在32位下访问到内存区域的前4个字节,在64位下访问到前8个字节?
最近在写项目的时候遇到这样一个场景:需要管理多个空闲的内存块,把它们以链表的形式连接起来,那就需要在内存块的头4个字节(32位下)存放下一个内存块的地址。原创 2023-03-31 23:32:19 · 464 阅读 · 0 评论 -
使用C++11的Thread和condition_variable实现单例模式的线程池
首先我们来看一下线程池的大致结构,这里先不实现单例模式,线程池使用了模板类,可以用于任意类型的任务对象,其中有一些成员变脸,_task_queue用于保存线程池中要执行的任务,_count表示线程的数量,_isStop表示线程池是否要停止运行,停止线程执行任务,还有一个互斥锁用于保证线程对任务队列的访问时安全的,条件变量用于当任务队列为空时,让线程等待;tips:任务队列中的任务是T类型的指针,如果任务执行完就不需要的话,我们可以在这里delete释放任务对象占用的空间,当然也可以由主线程去释放。原创 2023-03-14 00:10:14 · 427 阅读 · 1 评论 -
关于new和delete的一些思考,为什么不能在析构函数中调用delete释放对象的内存空间,new和delete的原理
最近在写代码的时候,觉得每次new出来的对象都需要去delete好麻烦,于是直接把delete写到了析构函数中,在析构函数里面写了句delete this,结果调用析构函数的时候死循环了,不是很理解原因,于是去研究了一下。原创 2023-03-08 17:10:32 · 1039 阅读 · 0 评论 -
序列化和反序列化~如何实现自定义协议?jsoncpp的使用
什么是协议?双方约定好的结构化的数据,本质就是协议的表现。(相当于我们约定好,哪个字段是昵称,哪个是头像,哪个是消息等等)实现一个简易的计算器服务,能处理client发送过来的加减乘除等基本计算请求。client每次向server发送的请求结构包含两个操作数和一个操作符的字符串;而server每次给client的请求包含一个code表示状态码,一个result表示计算结果。因此,我们可以在自定义协议文件中定义如下两个结构体,这就是我们自己定义的协议,server和client都必须遵守。原创 2023-02-12 17:23:02 · 637 阅读 · 0 评论 -
HTTP协议基础知识(URL,请求响应格式,状态码,方法,cookie和session等)
首先我们需要知道以下几点,1.我们请求的图片,html,css,js,视频,音频,标签,文档,等这些都称之为"资源"。2.服务器后台,是用Linux做的。3.IP + Port唯一的确定一个进程。4.公网IP地址是唯一确认一台主机的,而我们所谓的网络"资源"都一定是存在于网络中的一台Linux机器上。Linux或者传统的操作系统,保存资源的方式,都是以文件的方式保存的。单Linux 系统,标识一个唯一资源的方式,就是通过路径5.所以:IP+Linux路径,就可以唯一的确认一个网络资源。原创 2023-02-10 14:38:57 · 1020 阅读 · 0 评论 -
Linux下socketAPI的使用,实现udp和tcp通信
/ 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)一个文件描述符,执行socket()函数创建套接字的时候,相当于把网络以文件的形式打开了。创建失败就返回小于0的数// 绑定端口号 (TCP/UDP, 服务器)如果绑定成功返回0,绑定失败返回-1注意:bind(sock, (struct sockaddr*)&local, sizeof(local))传入第二个参数的时候需要进行强转。原创 2023-02-04 17:54:08 · 1470 阅读 · 0 评论 -
多线程~实现一个自己的线程池,以及基于单例模式的线程池
池化技术本质上都是为了提高效率。线程池也是同理,提前准备好一些线程,用来随时处理任务(处理完任务又放回池子),就称为线程池。为了提高效率。避免了在处理短时间任务时由于创建与销毁线程影响任务处理效率如果没有线程池,比如用户向服务器发起请求的时候,服务器收到请求需要先创建线程(需要耗时),然后再给用户提供服务,这对用户来说就是不必要的时间消耗。因此,可以提前准备好一些线程,服务器收到用户请求的时候马上就可以从线程池拿出一个线程来给用户提供服务。原创 2023-01-17 12:10:30 · 1050 阅读 · 0 评论 -
多线程~POSIX信号量实现生产者消费者模型,PV操作
信号量本质就是一把计数器,描述临界资源中资源数目的大小,合理使用可以达到对临界资源进行预订的目的。(最多能有多少资源分配给线程)多线程预定资源的方法:临界资源如果可以被划分成为一个一个的小资源,如果处理得当,我们也有可能让多个线程同时访问临界资源的不同区域,从而实现并发并行的访问临界资源。原创 2023-01-13 21:48:54 · 825 阅读 · 0 评论 -
多线程-生产者消费者模型,条件变量的使用,阻塞队列的实现
因为我们在调用pthread_cond_wait的时候,一般都是占用着锁的(一般都是检测到临界资源,发现不满足条件,才会调用这个wait方法),此时如果不把锁释放,直接wait的话,那就死锁了,其他线程也无法获取锁,修改这个临界资源。当消费者Pop取走任务的时候,同理要判断队列是否为空,如果空了就让消费者等待,然后加锁对队列进行操作,最后唤醒生产者来生产队列中的任务。暂时等待,而不是一直尝试抢票,如果一直抢票,抢票线程会一直获取锁,然后判断是否有票,没票,释放锁,不停循环这个操作,做无用功,而且占用锁。原创 2023-01-05 18:45:48 · 532 阅读 · 0 评论 -
操作系统~Linux~线程的互斥,mutex互斥锁的使用及其原理
1.临界资源:凡是被线程共享访问的资源都是临界资源(多线程、多进程打印数据到显示器,显示器就是临界资源)2.临界区:代码中访问临界资源的代码(在代码中,不是所有的代码都是进行访问临界资源的。而访问临界资源的代码区域我们称之为临界区)3.对临界区进行保护的功能,本质就是对临界资源的保护。方式:互斥或者同步4.互斥:在任意时刻,只允许一个执行流访问某段代码(访问某部分资源〉,就可以称之为互斥5.同步:一般而言,让访问临界资源的过程在安全的前提下(一般都是互斥and原子的),让访问资源具有一定的顺序性(具有合理性原创 2022-12-31 16:40:00 · 2363 阅读 · 0 评论 -
操作系统~Linux~线程控制,POSIX线程库的使用
功能:创建一个新的线程原型int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);参数返回值:成功返回0;失败返回错误码pthread_self()函数可以返回当前线程的id,这是一个地址,所以我们用十六进制打印这个地址结果分析 可以看到,主线程的线程id和新线程的线程id是不同的,而且是地址,同时我们可以发现参数zebra成原创 2022-12-26 18:08:03 · 768 阅读 · 1 评论 -
Linux操作系统~进程崩溃的原理是什么?信号的产生方式有哪些?
signal函数:修改进程对信号的默认处理动作signum:表示要修改几号信号handler表示要做出的处理动作函数e.g.://传入的信号表示要对几号信号进行自定义处理{//打印执行的是几号信号的处理函数,打印执行}int main(){//通过signal注册2号信号的处理动作,改成自定义处理动作while(1){sleep(1);}return 0;}原创 2022-12-09 20:01:05 · 1794 阅读 · 0 评论 -
Linux操作系统~匿名管道和命名管道的使用及其原理分析
1.创建子进程的时候,PCB也是要自己有一份的,同样里面的files_struct也是要自己独立有一份的(这些都属于进程的数据结构,进程是具有独立性的)2.而通过文件描述符找到的struct_file则是不需要给子进程拷贝一份的,因为这个是属于文件的,和创建进程没有关系。3.调用write方法的时候,是系统调用,会先将数据放在文件的内核缓冲区(不是C语言层面的缓冲区),底层定期的将缓冲区中的内容写到磁盘中。原创 2022-12-02 20:52:36 · 936 阅读 · 0 评论 -
Linux操作系统~尝试自己制作并使用动静态库
之后给别人用的时候,库文件和头文件都给,这样库文件就可以封装起来(转换成二进制文件)了。如果我们没有打包成库,.h文件(include的时候要写正确路径,或者借用环境)和.cc文件是分离的,此时编译的时候需要把.cc文件也要带上,否则会报错。为什么要这么设计呢?静态库在运行的时候不需要找库,所以运行的时候直接./mytest就行了,在编译的时候需要指定。动态库在运行的时候需要找库,所以除了在编译期间告诉编译器库在哪里,库的真实名字:去掉lib前缀,去掉.a...,so...后缀,剩下的就是库名称!原创 2022-11-22 23:53:07 · 957 阅读 · 0 评论 -
Linux操作系统~进程替换,exec系列函数的使用
这六个函数(加上execvpe一共七个)实际上只是参数上的不同,最终都是调用execve,系统给我们提供的实际上只有这一个接口,其他接口都是在此基础上封装出来的。(通过man手册也可以发现execve在man手册的2区内,表示是系统调用)原创 2022-11-15 21:10:26 · 1300 阅读 · 0 评论 -
C语言实现一个进度条小程序
最近学习了Linux的常用命令,还有vim的基本使用,想着在Linux下写个简答的小程序玩玩,顺便熟悉一下vim编辑器的使用。这里写一个进度条的小程序本来想直接循环输出100个符号,但是还想着加一个可以显示当前加载的进度条百分比的效果,于是想到用while循环,每次重新打印进度条的内容,但是如何做到在同一行重复打印呢?我查了下资料,发现使用回车符\r可以回到当前行的开始,还发现了换行和回车实际上是两个不同的概念。回车是回到当前行的开始;原创 2022-10-30 14:42:56 · 1654 阅读 · 0 评论 -
C++~类和对象总结(万字大总结,值得收藏)
我们不写,编译器默认生成的构造函数我们写的无参构造函数我们写的全缺省构造函数(全缺省最常用,因为使用的时候既可以传参数也可以不传参数)C++把类型分成内置类型(基本类型包括指针)和自定义类型。默认生成构造函数对于内置类型成员变量不做处理,对于自定义类型成员变量才会处理(会调用这个自定义类型成员的默认构造函数)原创 2022-10-21 16:43:56 · 2190 阅读 · 11 评论 -
LeetCode416-分割等和子集(经典01背包问题—动态规划)
本题要我们判断是否能将一个集合刚好分成两个总和相等的子集,首先我们可以算出总和,然后看能否被2整除如果不能被2整除,那必然无法分成两个总和相等的子集,直接返回false。如果能被2整除,我们就尝试能否找到一个集合,总和为sum/2,找到就返回true,找不到就返回false那怎么找呢,这里就可以将这个问题转化成一个01背包问题,首先01背包问题的数组dp[i][j]表示的是表示在0-i个物品中,往容量为j的背包,放价值最大的物品。在本题中,集合中的数据就可以看成是一个个的物品,物品的重量和价值都是数原创 2022-10-18 23:11:54 · 1058 阅读 · 1 评论 -
C++~LeetCode18. 四数之和-思路-逐行注释
3.然后计算四数之和,如果四数之和>target,则让right--(因为数组是有序的,所以让最后一个数缩小,四数之和也就会减小),如果原创 2022-10-01 22:32:04 · 127 阅读 · 0 评论 -
C++~LeetCode216-组合总和 III(回溯)
接着我们要确定传入的参数,题目给出的n和k以外,我们还需要一个nowSum来表示当前结果集中的和是多少,startNum表示现在从第几个数开始往后访问,因为组合是无序的,这里startNum的目的就是为了防止重复的组合出现。我们把整个问题的求解看成一颗树,树上的每条路径都是问题的一个可能的解,接着我们就是用for循环遍历当前层的集合元素,也就是当前层的结点,首先从startNum这个数开始,加入结果集,然后递归的去子树中寻找合适的结果集,找完以后返回。该列表不能包含相同的组合两次,组合可以以任何顺序返回。原创 2022-10-12 17:03:02 · 142 阅读 · 0 评论 -
C++~LeetCode49. 字母异位词分组-思路-逐行注释
思路将字母出现的频次作为键,map,这里的string表示的是字母出现的频数,string hash= string(26,0),初始化字符串hash,26个字符,分别表示a-z出现的次数。 然后将每个字符串中字符的出现次数统计一下,以当前统计字母出现次数的字符串作为键,将当前字符串插入到hashMap中键所对应的vector里面。 最后将hashMap里面的内容存放到ans里面就可以了。在遍历的时候使用了原创 2022-09-23 21:48:44 · 122 阅读 · 0 评论 -
C++~继承总结
继承(inheritance)是面向对象中使代码可以复用的最重要的手段,继承让我们在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,叫做子类。继承的实现过程是一个从一般到特殊的过程,体现出了面向对象设计的层次结构。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用(+复用+=),继承是类设计层次的复用(student和teacher都有名称,将名称定义在Person类里面,让student和teacher继承person)。原创 2022-10-14 23:40:49 · 434 阅读 · 0 评论 -
C++~模板-泛型编程(函数模板,类模板,模板特化的使用和原理)
函数模板和类模板不支持声明和定义放到两个文件中,会出现链接错误普通的函数和类的话是可以的模板不支持声明和定义分别放到xxx.h和xxx.cpp中,一般是要放到一个文件中。有些地方就会命名成xxx.hpp,寓意就是头文件和定义实现内容合并—起但是并不是必须是.hpp,.h也是可以的,最好是.hpp。原创 2022-10-09 13:17:11 · 659 阅读 · 0 评论 -
C++~new/delete,new[]/delete[]以及C语言中的malloc/free的区别和联系,new/delete底层原理分析
1.new/delete使用起来比malloc/free更加简洁,new申请空间的时候,只需要给出空间的类型即可,如果申请的是多个元素的连续空间,再给出元素的个数即可。而malloc需要自己计算空间的大小。2.malloc是函数,返回值类型为void*,所以使用的时候需要显示地强制类型转换,new是操作符,后面直接跟上空间类型和对应类型元素的个数即可。3.申请空间失败时,malloc返回的是NULL,new则是抛出异常。原创 2022-10-03 23:18:30 · 406 阅读 · 0 评论 -
C++~类和对象基础(构造函数,析构函数,拷贝构造函数,赋值运算符重载详解)
默认构造参数(不用传参就可以调用的构造函数就是默认构造函数)我们不写,编译器默认生成的构造函数我们写的无参构造函数我们写的全缺省或者半缺省构造函数(全缺省最常用,因为使用的时候既可以传参数也可以不传参数)C++把类型分成内置类型(基本类型包括指针)和自定义类型。默认生成构造函数对于内置类型成员变量不做处理,对于自定义类型成员变量才会处理(会调用这个自定义类型成员的默认构造函数)原创 2022-09-26 19:10:29 · 831 阅读 · 0 评论 -
C++~auto关键字,auto使用细则,存在的意义,不能使用的场景
auto不能作为形参类型,因为编译器无法对a的实际类型进行推导,只有在调用函数的时候,才会进行传参,在这之前不知道auto类型的话,栈帧的大小,以及给a变量分配空间的大小都不知道,在编译的时候就会报错。当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。c语言规定数组在传参的时候是用指针传递的,此时的array是指针,而不是数组,范围是不确定的。下面的方式也是错的,当数组作为参数传入函数的时候,原创 2022-09-22 11:10:21 · 1372 阅读 · 0 评论 -
C++~带你了解引用,引用特性,常引用,使用场景
这里需要注意,如果返回的变量出了函数作用域以后就不存在了,那么不能使用引用返回,引用指向的变量在出了函数以后就已经销毁了,此时在函数外面使用引用的话,会修改已经被销毁的内存区域的值,是很危险的,属于非法操作。当然,如果变量是static的变量就可以,因为static变量的static的生命周期是整个程序运行期间,所以就算出了函数作用域,其内存空间也不会被销毁,此时通过引用对其修改就是合法的。而printf函数是读操作,将变量的值读取并输出,此时是读操作,不需要修改变量a的值,也就不需要加地址了。原创 2022-09-20 22:49:45 · 559 阅读 · 0 评论 -
C++~从编译链接的过程看为什么C++支持重载?externC有什么用?
C++为什么支持重载?externC有什么用?从编译链接的过程中就可以找到答案,首先聊一聊编译链接的过程总共分为四个过程——预处理,编译,汇编和链接。--它做的主要是头文件展开、宏替换、条件编译、去掉注释等工作。预处理结束后,会生成预编译文件(.i)--这个阶段会检查语法,将经过预处理后的预编译文件转换成特定汇编代码,生成汇编文件(.s)--将编译阶段生成的汇编文件转化成机器码,生成可重定位目标文件(.o/.obj)。--将多个目标文件连接成最终可执行目标文件(.out/.exe)。原创 2022-09-17 22:52:21 · 399 阅读 · 0 评论 -
C++~结构体/类的对齐规则详解,为什么要对齐
类的对齐规则和结构体很相似,这里以结构体举例结构体对齐规则1. 第一个成员在与结构体偏移量为0的地址处。2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。64位操作系统默认的对齐数为8,32位的为43. 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。原创 2022-09-14 23:00:52 · 859 阅读 · 0 评论 -
Float/Double内存结构分析,取值范围、有效位数与内存结构的关系详解(取值范围如何得出?)
偶然间看到float的取值范围是-3.4*10^38到3.4*10^38,然后有效位数是6位或者7位(和编译器有关),突发奇想,想知道float和double的范围和有效位数是如何计算出来的,了解了一下,大概做个总结。首先说一下Float。原创 2022-09-06 15:06:59 · 2129 阅读 · 1 评论