自定义博客皮肤VIP专享

*博客头图:

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

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

博客底图:

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

栏目图:

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

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(157)
  • 资源 (1)
  • 收藏
  • 关注

原创 C++中普通函数指针和成员函数指针的区别

1.普通函数指针存在隐式转换:typedef int (*pFun) (int, int);int add (int x, int y) { return x + y;}这时候,可以通过pFun p = add完成赋值,也可以通过pFun p = &add完成赋值。2.非静态成员函数不存在隐式转换:class A;typedef int (A::*pClassFun)(int, int); // 成员函数指针类型class A { public: int add(int

2021-09-07 15:45:12 453

原创 epolloneshot是怎么实现的

在内核把发生的事件和文件描述符都返回给用户空间之后,会判断if (epi->event.events & EPOLLONESHOT),如果为真,那么就会将events修改为EP_PRIVATE_BITS。而这个位图定义为:#define EP_PRIVATE_BITS (EPOLLWAKEUP | EPOLLONESHOT | EPOLLET | EPOLLEXCLUSIVE)也就是说,用户自定义的关注事件都被清除了。...

2021-09-05 15:33:21 382

原创 全连接队列和半连接队列

半连接队列syn-cookie打开的情况下服务器接收到第一次握手的消息后,不会立刻将相关信息放进半连接队列,而是根据对面发过来的报文计算自己的SYN初始序列号。利用下面几个部分:客户端IP、客户端端口号、服务端IP、服务端端口号,这4个部分计算一个哈希值一个缓慢增长的时间戳t客户端发来的SYN序列号客户端发来的MSS协商值利用这4个部分计算一个序列号,作为服务端的初始序列号,发送给客户端。从客户端收到第三次握手的信息,先提取它的ACK序列号,然后减1,就应该是服务端计算出来的初始序列号

2021-09-04 21:34:55 513

原创 glibc中的malloc

glibc的malloc使用的是ptmalloc分配器。使用chunk来管理内存单元.ptmalloc通过chunk的数据结构来组织每个内存单元。当我们使用malloc分配得到一块内存的时候,这块内存就会通过chunk的形式被记录到glibc上并且管理起来。上图是被分配的chunk。用户获得的指针是mem指针。所以实际上使用的内存大于用户申请的内存。上图是未分配的chunk。未分配的chunk都串联在bins数组中。bins数组ptmalloc一共维护了128bin。每个bins都维护了大小相

2021-09-04 18:11:59 1202 1

原创 C++中const变量都存在哪里?

const全局变量const全局变量是存储在.rodata节中的// b.cpp文件的内容#include <iostream>#include <string>using namespace std;const int yanjia = 10;int main() { return 0;}上面的代码编译出可执行文件,然后通过objdump -t这个可执行文件,能看到所有符号所在的段。可以看到yanjia这个全局变量(名字被编译器换成了_ZL6ya

2021-09-03 23:32:57 2797 2

原创 malloc得到的指针是可以进行delete的

#include <iostream>using namespace std;class A {public: int a; A() { a = 0; } ~A() { cout << "destructed!" << endl; }};int main() { A* a= (A*)malloc(sizeof(A)); delete a; return 0;}

2021-09-02 22:40:32 902

原创 delete a[]的时候是怎么直到要删除的数组的大小的?

new可以直接产生一个数组#include <iostream>using namespace std;int main() { int* a = new int[4]; delete[] a ; return 0;}new的中括号里必须填一个字面值常量。析构也需要调用对应的delete []方法。所以delete [] a的时候是怎么知道要删除的数组的大小的?其实是在new int[4]的时候,多分配了4字节的空间,在这4字节的空间中存放了数组的大小,

2021-09-02 22:36:38 572

原创 内部链接和外部链接

内部连接如果一个名称对编译单元(.cpp)来说是局部的,在链接的时候其他的编译单元无法链接到它且不会与其它编译单元(.cpp)中的同样的名称相冲突。例如static函数,inline函数等(注 : 用static修饰的函数,本限定在本源码文件中,不能被本源码文件以外的代码文件调用。而普通的函数,默认是extern的,也就是说,可以被其它代码文件调用该函数。)外部连接如果一个名称对编译单元(.cpp)来说不是局部的,而在链接的时候其他的编译单元可以访问它,也就是说它可以和别的编译单元交互。 例如全局变量

2021-09-02 22:14:39 222

原创 在共享内存中使用标准库容器的思路

可以自定义一个在共享内存中分配内存的allocator,用这个allocator来构造标准库容器。怎样通知另一个进程,自己的容器在哪里呢?可以将容器放在共享内存中固定偏移的地方。

2021-09-02 21:33:15 376

原创 右值引用、std::move、std::forward

左值和右值能取地址、有名字的是左值。不能取地址、没有名字的是右值。比如对于&(b + c);这样的表达式就不能通过编译,因为b+c是右值,不能取地址。C++11中又将右值进一步区分为纯右值和将亡值。纯右值:临时变量(比如a = b + c中的b + c)和字面值(比如a = 2中的2)将亡值: 1)返回右值引用的函数的调用表达式; 2)转换为右值引用的转换函数的调用表达式右值引用是左值还是右值?有名字的右值引用是左值,没有名字的右值引用是右值。// 形参是个右值引用

2021-09-02 20:51:10 283

原创 析构函数不应该抛出异常,构造函数不应该抛出异常

如果析构函数抛出异常,并且没有在析构函数内部进行捕捉,那么程序控制流会跳出析构函数。于是析构函数中抛出异常之后的语句不会被继续执行,可能会造成内存泄露。

2021-09-02 16:08:11 225

原创 operator new和placement new

C++中的new关键字完成的工作是两步:调用operator new函数,分配内存空间。默认的operator new函数可能会抛出异常。这也是new关键字会抛出异常的原因。用户可以重载这个函数,于是new关键字就会调用这个自定义的operator new函数。调用placement new函数。这个函数是在已经分配好的空间上调用构造函数。调用方法形如:new (ptr) A(5);。placement new也是重载的一个operator new的版本,它是不能被用户替换的。让一个类只能被静态创

2021-09-02 15:17:05 212

原创 类的列表初始化

类的成员初始化有两种方法:列表初始化,就是冒号后的初始化过程类内部初始化。就是在构造函数的大括号里面用等号。第二种方法慢,是因为即使没有列表初始化,类的成员也会调用自己的不含参数的默认构造函数进行初始化,然后在大括号中用等号就是多做了一次初始化和一次拷贝。多调用了一次包含参数的构造函数和一次拷贝赋值函数。而列表初始化,是直接调用成员的相应版本的构造函数,没有再进行赋值。有4种情况必须使用列表初始化:类的成员是引用类型类的成员是const类型类的成员没有默认构造函数。如果不使用列表初始化

2021-09-02 14:58:28 1256

原创 C++和C中的静态变量

全局静态变量和全局变量的区别:全局静态变量的作用域仅仅在本文件中C语言中的局部静态变量:在编译的时候就分配了内存,在main函数执行前进行初始化C++语言中的局部静态变量:在第一次使用的时候分配内存并进行初始化。保证线程安全,即可以用在线程安全的单例模式中。C++语言中的静态成员变量:在编译的时候分配内存,在main函数执行前进行初始化。...

2021-09-02 14:20:57 413

原创 lego-loam和autoware一起使用

一、下载和安装gtsam:1.下载gtsam的源代码git clone https://bitbucket.org/gtborg/gtsam.git2.编译安装gtsammkdir buildcd buildcmake ..sudo make install二、下载lego-loam:1.建立工作空间mkdir -p catkin_ws/srccd catkin_ws/srccatkin_init_workspace2.下载源码git clone https://gitee

2021-07-25 14:28:13 642 1

原创 malloc的过程

内存中堆和栈的大小在加载的时候是固定的也就是说,可执行文件加载到内存中的时候,就给栈和堆划分了固定大小的空间,vm_area_struct结构体指明了一个连续区域的头地址和尾地址。所谓的栈和堆的动态增长,并不是指堆和栈空间的头尾指针发生变化,它们是不会改变的。变的只是两个指针:栈顶的指针和堆顶的指针。malloc的两种方法malloc当然会先从空闲页面中找合适大小的页面。但是如果找不到,需要分配新的页面。这时候有两种方法:brk()调用,将堆顶的指针brk往上推mmap()调用,在文件映射区域

2021-07-23 17:50:16 620

原创 Linux中伙伴算法分配的是物理内存

malloc分配的是虚拟页面。在第一次访问时产生缺页中断,操作系统负责分配物理页面,映射到这些虚拟页面上。而伙伴算法解决的就是怎样分配物理页面。伙伴算法是一个门户,用户控件的malloc、内核空间的vmalloc和kmalloc都通过伙伴算法来分配物理页面。...

2021-07-23 15:28:01 186

原创 共享内存的实现原理

shmget物理内存_共享内存的几点总结IPC共享内存shmget函数创建一片共享内存空间,返回这片共享内存的id。shmat调用能将这片共享内存空间映射到自己进程的虚拟内存空间,返回虚拟地址。两个进程要通过共享内存进行通信,必然要映射到同一片物理内存。如何保证这一点呢?shmget函数有一个参数是shm_key,它能唯一的标志一片物理内存。第一个进程用某个key创建了共享内存空间,而另一个进程使用相同的key调用shmget时,不会创建新的共享内存,而是直接返回前一个进程创建的。IPC共享内存实

2021-07-21 21:32:49 723

原创 两种内存映射:普通文件和匿名文件

普通文件将可执行文件加载到内存中,进行的映射是普通文件的映射。比如.data段、.bss段、.text段等。在cpu第一次访问相关页面的时候,发生缺页中断,才真正将磁盘中的页面缓存到物理内存中。对于.bss段,不占据实际的磁盘空间,只在段表中记录大小,在符号表中记录符号。当文件加载运行时,才分配空间以及初始化。这就是csapp中提到的如果区域比文件区大,就用零来填充这个区域的余下部分。匿名文件堆、栈都是匿名文件映射。虚拟页面并不和磁盘中的文件发生映射。CPU第一次引用一个区域内的匿名页面时,发生缺页

2021-07-21 17:59:42 1368

原创 vm_area_struct的作用:Linux缺页处理

进程的task_struct中存了mm_struct的指针mm,task_struct中存了vm_area_struct的链表结构每个vm_area_struct都代表虚拟内存空间中一段连续的、具有相同访问权限的区域。包含下面的成员:vm_start:指向这个区域的起始处vm_end:指向这个区域的结束处vm_prot:描述这个区域所有页的读写许可权限vm_flags:描述这个区域内的页面是与其他进程共享的还是私有的vm_next:指向链表下一个区域结构当访问某个页面发生缺页中断,需要查询

2021-07-21 17:45:35 391

原创 Core i7的四级页表翻译过程

虚拟地址中真正用于寻址的部分是48位物理地址是52位每个物理页面的大小是4KB,因此在一个页面中寻找某个字节,需要一个12位的数字定位(因为212=40002^{12} = 4000212=4000)。所以虚拟地址用36位来关联一个物理页面,用12位来关联这个物理页面中具体的某一个字节。物理地址用40位定位一个物理页面,用12位来定位这个物理页面中具体的某一个字节。所以物理内存中,每个页面的基地址都是4KB对齐的,也就是说每个页面的第一个字节的物理地址,低12位都是0。每一级的页表,大小都是一个物理

2021-07-21 17:09:35 737

原创 一级页表在物理内存上是连续的

困惑了很久的问题。task_struct中有一个成员mm_struct,mm_struct有一个成员pgd,每次要进行进程切换的时候,通过pgd来修改cr3寄存器中的值,将它修改为进程的一级页表的物理地址。每张页表在物理内存上是连续的,所以获得这个地址之后,就可以访问一级页表中的每一项了。pgd是一个指针,指向的是一级页表。但是它并不是物理地址,而是虚拟地址。所以在修改寄存器cr3的时候,需要先转换为物理地址:#define cpu_switch_mm(pgd,mm) cpu_do_switch_mm

2021-07-06 00:52:51 835

原创 迭代和递归的区别

看到有面经提到了这道题,之前简单想了下,拿二叉树前序遍历来说,迭代和递归本质上都用到了栈,只不过后者用的是进程栈,所以速度比较慢,因为这个栈中还要存返回地址、局部变量和寄存器等。但是今天又反应了以下,二者的差别不仅仅是速度的问题,在C++中,迭代使用的栈是标准库提供的,是配接器,底层使用的是vector,而vector的数据是存在堆上的(通过标准库的allocator分配空间);而如果是递归,使用的是栈区,比堆小很多,如果递归次数过多,造成栈帧太多,很容易栈溢出。所以迭代能比递归进行更多的层数。...

2021-07-05 21:01:39 276

原创 用等待队列实现阻塞的系统调用

什么是等待队列?等待队列可以看作是双向链表,但队列头和队列成员不是同一种数据结构上图中,能看出队列头包含一个自旋锁(用于互斥访问,保证安全)和两个指针,而等待队列的成员包含一个flags域和一个task域以及两个指针。队列头可以看作是某一种资源,而队列的成员可以看作是请求这个资源而不得的人(进程)。怎么用等待队列实现阻塞?一个进程请求某项资源的过程(比如read某个文件)如下:1.用当前的进程描述块(PCB)初始化一个等待队列成员,表示一个等待任务,让这个新创建的队列成员的task域指向当前的

2021-06-19 23:35:49 189 3

原创 单例模式中的那些静态变量:静态成员和局部静态变量的区别

在网上看到Meyers提出的线程安全的单例模式,有点搞不懂其中的原理。Meyers提出的单例模式的代码:#include <iostream>class Singleton{public: ~Singleton(){ std::cout<<"destructor called!"<<std::endl; } Singleton(const Singleton&)=delete; Singleton&amp

2021-06-03 00:22:31 1181 4

原创 unordered_map查询的时候最好还是先使用count

遇到的问题是在leetcode刷题的时候,使用了哈希表。基本上是一边修改哈希表一边查询哈希表。但是我的运行时间却特别慢。然后看了别人的代码,发现差别就是我在查询的时候直接用ans += hashmap[k]这种方式,而别人都会先判断if (hashmap.count(k) > 0),然后再加到ans中。虽然我的做法结果不会出错,但是每次查询,都调用哈希表的operator()运算符,那些本来不在哈希表中的数字并且以后也用不到的键都被插入了,于是需要开辟空间存储值,可能在某些时候由于冲突还需要重新调整哈

2021-05-29 21:11:42 358

原创 优先级队列不要用来存实时更新的值

在做692. 前K个高频单词这道题的时候,我想的是用一个哈希表存储各个单词出现的次数,用优先级队列存储出现次数最多的K个单词,自定义优先级队列的比较函数为比较哈希表中每个单词的次数。然后一次遍历,构建哈希表的同时不断向优先级队列插入和弹出,保持优先级队列只有出线次数最多的K个元素,并且头部是出线次数第K多的元素。这种方法最后证明是错误的。主要原因是哈希表是在线更新的,这种更新导致过程中优先级队列不是一个正确的堆。比如原来的堆是[“yan", "jia", "shi", "sha", "cha"],三个字符

2021-05-20 15:57:11 190 1

原创 set自定义比较函数很容易出现错误

set的多个成员函数都可能因为自定义的比较方式出现错误。对于insert,由于set要求不能出现重复的元素,所以如果发现要插入的元素已经存在了,就不会进行插入。而set使用comp来判断两个元素是否相等:如果comp(a, b)返回false并且comp(b, a)也返回false,那么认为a和b是相等的。如果我们自定义的comp不能实现这个功能,那么插入就会出现异常。比如明明b不在set中,但是由于set中有某个元素a满足!comp(a,b) && !comp(b, a),set误以为a

2021-05-05 20:36:37 568

原创 配接器总结

配接器实际上是一种设计模式:将一个class的接口转换为另一个class的接口,使原本因接口不兼容而不能合作的classes,可以一起运作。container adapters用于修改容器接口的配接器称为container adapter。stl提供的两个容器queue和stack,其实都只不过是一种配接器。iterator adapters应用于迭代器身上的配接器叫做iterator adapters。包括insert iterators、reverse iterators和iostream

2021-04-17 17:21:46 130

原创 p442:这里的bound friend function template是什么意思?

一个模板类可能包含友元声明。这个友元可能是模板,也可能不是。假如友元不是模板,那么当前模板类的所有实例都授权友元访问。假如友元是模板,那么当前模板类可以授权所有友元模板实例,也可以只授权给特定实例一对一友好关系:友元用类模板的模板形参作为它自己的模板实参。template <typename> class Blobptr;template <typename> class Blob;template <typename T> bool operator==(c

2021-04-12 23:32:56 386 1

原创 仿函数

1.现在的名称是函数对象(function objects):一种具有函数特质的对象。为了实现“行为上类似函数”,其类别定义中必须重载()符号。2.有3种配接器,其中一种配接的是仿函数。为了能实现配接功能,每一个仿函数都必须定义自己的相应型别。 而标准库内置定义了两个class,分别代表一元仿函数和二元仿函数,它们都没有data members和member functions,只有一些型别的定义。任何仿函数,只要依个人需求选择继承其中一个class,就自动拥有了那些相应型别,也就自动拥有了配接能力。3

2021-04-09 13:04:07 185

转载 stl算法总结(感觉有意义的)

排序1.partial_sort和partial_sort_copy:它的功能是将[first,last)中的middle-first个最小元素以递增顺序排序,放在[first, middle)中,剩下的元素不保证顺序,放在剩下的位置中。使用heap容器相关算法。make_heap(first, middle);for (RandomAccessIterator i = middle; i < last; ++i) { if (*i < *first) _pop_heap(fir

2021-04-07 23:08:51 145

原创 p363:碾转相除法的证明

问题:证明gcd(m,n)=gcd(n,m%n)gcd(m, n)=gcd(n, m\%n)gcd(m,n)=gcd(n,m%n)。证明:设r=m%nr = m\% nr=m%n,k=m/nk=m/nk=m/n设c=gcd(m,n)c=gcd(m,n)c=gcd(m,n)∴\therefore∴ m=x1cm=x_1cm=x1​c,n=x2cn=x_2cn=x2​c∴r=m−k∗n=x1c−kx2c=(x1−kx2)c\therefore r=m-k*n=x_1c-kx_2c=(x_1-kx_2

2021-04-05 19:04:44 136

原创 p363:rotate的random access iterator版本是怎么一回事?

1.rotate操作实际上可以看作是左移操作。比如要求将一个序列的前m个元素和后面的元素交换位置,其实相当于将所有元素左移m位,如果移到了头,就自动跳到尾部接着移。2.于是有这样一种算法,先让第iii位元素等于第i+mi+mi+m位,然后让第i+mi+mi+m位元素等于第i+2∗mi+2*mi+2∗m位元素。当然这里要使用模操作来应对数组越界。这样就相当于元素左移m位了。3.但是这样有一个问题,比如对于[a, b, c, d, e, f],要求将所有元素左移两位。我们从i=0i=0i=0开始,然后iii

2021-04-05 17:35:37 152

原创 stl中的一些算法

copy算法各种方法,强化效率。1.首先提供一个完全泛化版本:template <class InputIterator, class OutputIterator>inline OutputIterator copy(InputIterator first, InputIterator last, OutputIterator result){ return __copy_dispatch<InputIterator, OutputIterator>

2021-04-04 22:34:47 125

原创 p346:根据迭代器类型进行分派的函数

迭代器有多种类型,ForwardIterator只能单向递增,而BidirectionalIterator可以双向运动。而同一个函数可以根据迭代器的类型不同,采用不同的算法。所以这里一般都会有双层设计,暴露在最外层的函数会作为一个分派函数,它先萃取出迭代器的类型,然后作为参数传入另一个更底层的函数。这个底层函数是有多个版本的,编译器根据迭代器类型这个参数自动匹配合适的版本。这样就实现了不同迭代器采取不同算法的功能。...

2021-04-04 19:58:45 185

原创 p324 关于vector的测试

1.vector的构造函数中,接受两个迭代器的版本,经过层层调用,实际上就是使用了泛型算法copy。2.自定义的类型,即使它实际上具有trivial operaotr=,但是编译器不会自动检测这一点。编译器只能知道内置类型比如int、char、long等的trivial特性。如果我们想让自定义类型的travial特性被编译器识别,就必须自己为它做特性设定。具体来说,就是自定义一个__type_traits类的偏特化版本:<>struct __type_traits<C> {

2021-04-04 16:38:21 158

原创 p317 copy函数的输入区间和输出位置发生重叠,会有问题吗?

这要取决于迭代器所指的是什么类型了。假如迭代器是const char*、const wchar_t*类型,或者是T*类型,并且元素类型T具有travial assignment operator,那么copy的底层会使用memmove,这个函数会先将输入区间的所有内容先复制下来,然后再存进目的地,没有任何问题;但是如果底层没有使用memmove,那么它的拷贝过程是向前推进的,于是输入区间内后面的元素还没来得及拷贝,就被前面的元素给覆盖了。这时候的拷贝行为是未知的。...

2021-04-03 22:33:07 88

原创 p310 字典序是什么意思

“字典排列方式”对两个序列[first1, last1)和[first2, last2)进行比较。比较操作针对两序列中的对应位置上的元素进行,直到(1)相应位置上的元素不相等(2)有一个已经走到尾巴了停止时:(1)第一序列的元素比较小,返回true。否则返回false。(2)到达last1而未到达last2,返回true(3)到达last2而未到达last1,返回false(4)如果同时到达了last1和last2,说明所有元素都匹配。返回false。从上面的过程中,我们知道迭代器指向的类型必须

2021-04-03 21:02:04 173

原创 p310 迭代器的value_type是个什么类型?

这里能看出,value_type函数接收一个迭代器,返回的是这种迭代器所指向类型的指针。template <class Iterator>inline typename iterator_traits<Iterator>::value_type*value_type(const Iterator&) { return static_cast<typename iterator_traits<Iterator>::value_type*> (0)

2021-04-03 20:48:23 410

红绿灯单片机设计

自己设计的简单红绿灯单片机原理图,主要实现红绿灯自动变换和峰鸣

2018-01-11

空空如也

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

TA关注的人

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