volatile

用来告诉编译器不要对该变量做任何优化,编译器每次操作该变量时,一定要从内存中取出,而不是使用寄存器中与存在的值,因为值可能已经发生了改变。

应用场景:

  1. 并行设备的硬件寄存器(如状态寄存器)。
  2. 多线程中共享变量。

const

用于限定变量,函数和指针不可改变。

  1. const限定全局变量时,变量放在静态区。const限定局部变量时,变量放在栈区。
  2. const修饰类中的成员函数时,表示不可对类的对象更改,如果需要更改类中的成员变量,可以使用mutable关键字。

内联函数和宏定义

  1. 宏定义在预编译时展开,内联函数在编译时展开。
  2. 宏定义只是简单的文本替换,不进行参数的有效性检查。内联函数会进行类型检查,然后将函数体嵌入到目标代码中。
  3. 宏定义不加括号容易出错。内联函数没有普通函数的栈操作,效率很高,但是不正确的时候内联函数会导致代码体积过大。

static

静态全局变量,静态局部变量,静态成员变量,静态成员函数

  1. 静态全局变量和静态局部变量都放在静态区,只在声明时初始化一次,仅在本文件中可见,变量在程序运行期间一直存在。静态局部变量的作用域范围仅在局部,下次调用紧接着上次调用的结果值改变。
  2. 静态成员函数和静态成员变量没有this指针,必须通过类名才能访问。

malloc 和 new

  1. malloc和free是库函数,new和delete是运算符,可以重载。
  2. malloc申请内存时需要指定内存大小,返回一个void*类型,通常需要强转。new申请内存时无需指定内存大小,返回一个对应类型的指针。
  3. malloc不进行初始化。new在创建非内部类型的对象时,调用构造函数初始化,消亡时会调用析构函数。
  4. malloc申请失败时返回null指针,new申请失败时抛出异常。

指针和引用

  1. 引用创建时必须初始化,初始化后不可以改变。指针创建时可以不初始化,初始化时需要分配内存,初始化后也可以改变。引用比指针安全。
  2. 引用不存在空值引用,指针可以指向空值
  3. 引用的本质是指针常量,由编译器完成转换。

extern

  1. 可以被其他源文件调用。
  2. 告诉编译器,extern限定的代码使用C语言的编译和连接方式。因为C++支持重载,而C不支持,所以可能导致函数编译后的名称不一致,从而找不到对应的函数。

多态的原理

多态分为静态多态和动态多态。静态多态是通过重载和模板实现的,动态多态是通过继承和虚函数实现的。

动态多态是让基类的指针或引用指向派生类的对象实现的。定义为虚函数的基类,编译器会自动创建一个虚函数表,派生类在继承时会继承这个虚函数表,每个创建的对象拥有一个虚函数表指针,这个指针指向虚函数表,如果派生类重写了基类的虚函数,则对应的虚函数表入口地址发生了更新。虚函数表为所有对象所共享,通常放在代码段。

什么是进程

进程是分配资源的基本单位,是程序运行的实例。进程通常分为数据段,代码段和进程控制块。进程的地址空间相互独立,一个进程不可访问另一个进程的数据。进程切换时的开销相对于线程来说比较大,需要保存寄存器的值和刷新块表,切换地址空间等。

进程间的通信方式

  1. 有名管道pipe
  2. 无名管道fifo
  3. 消息队列
  4. 共享内存
  5. 信号量
  6. 信号
  7. 套接字

线程是什么

线程是CPU调度的基本单位。线程是进程的一个执行流程。一个进程中可以由多个线程。线程共享进程的地址空间。线程自身几乎不拥有系统资源,除了栈和寄存器。线程间的通信较为容易。但是需要同步。常用的同步机制有互斥锁,读写锁,信号量,条件变量。

线程共享

  1. 进程打开的文件描述符表
  2. 进程ID和组ID
  3. 信号的处理方式
  4. 打开的工作目录
  5. 进程的地址空间(堆区,栈区,共享区)

线程独享

  1. 线程ID
  2. 线程的寄存器
  3. 线程的栈
  4. 信号屏蔽字
  5. 线程优先级
  6. 错误返回码errno

信号

信号是一种不精确的通信方式。常用的信号有15号信号,杀死进程 29号进程 11号段错误

http和https的区别

https是安全版的http协议。

get和post的区别

get用来获取资源,post用来创建资源。

get将数据放在url地址栏中,使用问号与url分割,数据间用取地址符分割。post将数据放在请求体中。

安全性上get放在url容易暴露隐私信息,而post放在请求体中可以适当的避免。

get在传输的数据受url地址栏的限制,post不受这种限制

get的操作是幂等的,多次操作产生的影响相同,而post是非幂等的

B树和B+树的区别

B树是多叉平衡树,**M阶的B树,每个节点最多有M-1个关键字,**每个节点的关键字都按照从小到大的顺序排列,因此查询时可以使用二分查找法。

B树中所有叶子节点都位于同一层,每个节点都存有索引和数据。

B树的优点在于查询单个数据时,由于每个key都存有对应的date,查询到后可以直接取回。

B+树非叶子节点只存索引不存数据,每个叶子节点增加一个指向相邻接叶子节点的指针,所有的节点都存在叶子节点。B+树的优点在于范围查询时,可以利用相邻的指针获得指定范围内的数据。

网络拥塞

  1. 慢启动
  2. 拥塞避免
  3. 快速重传
  4. 快恢复

TCP和udp的区别

tcp面向连接的可靠的流式协议,具有超时重传和确认等机制。

udp面向无连接的报文协议,发出去就不管了,收则全收,丢则全丢。

智能指针

unique_ptr

shared_ptr

weak_ptr

四种强制类型转换

const_cast去掉const属性的转换,包括const指针和引用

static_cast用于低风险的转换,比如字符型转整形

dynamic_cast 用于具有继承关系的派生类和子类的转换,当基类转为子类时,如果转换失败会返回NULL

reinterpret_cast 任何类型都能转,风险较高

post和put的区别

post用来创建资源,put用来更新资源

post是非幂等的,put是幂等的。

8中请求方法:

http 1.0: get post head

http 1.1: put delete options connect trace

mmu内存管理器

主要作用:虚拟内存到物理内存的地址映射。 设置修改内存访问级别。

虚拟内存的作用

  1. 解决主存容量有限
  2. 分隔进程,保证进程空间彼此独立不受干扰
  3. 基于局部性原理进行页面替换

虚拟内存的大小由计算机的地址总线决定

cache名字和TLB命中没有必然联系,是两种独立的机制。

CPU和Cache之间交换的单位是字节,Cache和内存之间交换的单位是块。

coredump文件

gdb可以用于分析coredump文件。coredump文件含有进程被终止时内存/CPU寄存器和各种函数调用栈的信息。

产生coredump文件的原因:

  1. 内存访问越界
  2. 多线程使用了线程不安全的函数
  3. 多线程读写的数据未加锁保护
  4. 栈溢出

core文件没有符号表信息,必须结合可执行文件才可调试

模板特化

全特化:模板参数被指定未确定的类型

偏特化:模板参数没有被全部确定,需要编译器在编译时进行确定。只能偏特化类模板,不能偏特化函数模板。

别名模板和变量模板属于语法糖

元编程

编译时计算出运行时需要的常数,类型和代码的方法。

右值引用

右值引用指向要被销毁的对象。右值要么是字面常量,要么是在表达式求值过程中创建的临时对象。

move函数将左值转换为右值,调用move函数后源对象只能赋值或销毁。

override

override在子类中标记某个函数,表示想要覆盖已有的虚函数,如果没有覆盖,编译器会报错。

加作用域运算符调用特定类的虚函数

内联函数的优劣

  1. 优点:减少函数调用的开销,包括寄存器值的保存和实参的拷贝等。
  2. 缺点:增加函数体积,可能导致cache装不下,从而减少了cache的命中率。

inline只是一个请求,编译器有权拒绝。

拷贝构造函数

调用场景:

  1. 一个对象以值传递传参
  2. 一个对象以值传递的方式从函数返回
  3. 一个对象通过另一个对象初始化

空类

占有一个字节

有构造,析构,拷贝,赋值运算符,取地址运算符。

构造函数可以被重载,析构函数不可以被重载且不能带参数。

explicit

explicit取消隐式转换,类中构造函数默认是implicit

explicit关键字的作用是防止类构造哈桑农户的隐式自动转换,只对有一个参数的构造函数有效。

堆和栈的区别

  1. 申请方式不同。栈由操作系统自动分配,堆需要程序员自己申请。
  2. 生长方向不同。栈由高地址向地址生长,是一块连续的内存区域。堆由地址向高地址生长,是不连续的内存区域。在一个链表中记录空间内存地址。
  3. 分配速度。栈由系统分配,速度较快。堆使用new分配,速度较慢,且容易产生内部碎片。

C++ 和python的区别

python是解析性语言,无需编译,方便快捷,跨平台性很好。

C++是编译型语言,先编译后执行,编译后通常不能跨平台。

python使用严格的缩进来表示不同级别的代码块,在C++中使用花括号。

索引

select语句调用函数后就不会用到索引

static的作用

static可以用来修饰函数和变量。修饰全局变量和局部变量时都是放在静态区,static变量只初始化一次,在程序结束时销毁,全局和局部的区别在于作用域不同。static可以修饰普通成员函数,表明这个函数只在本文件中有效。static修饰类成员变量是,这些变量为这个类所共享,static修饰类成员函数时,也是所有对象共享这个函数,该函数中没有this指针。同时static类成员函数中只能调用static修饰的函数。

静态存储区

  1. 存放的static修饰的全局变量和局部变量,const修饰的变量以及字符串。

数据段和静态区的区别

数据段存放的是代码的二进制指令。静态区是变量。

虚函数的实现机制

每个含有虚函数的类都有一个虚函数表,类创建的对象都由编译器自动生成一个虚函数表指针来指向虚函数表。子类继承时会继承这个虚函数表,在子类中如果重写了父类定义的虚函数,这个虚函数中的对应内容会替换为重写的内容。当父类指针或引用指向子类对象时,调用对用对应的虚函数时会根据虚函数表指针找到虚函数表,然后从表中找到重写的虚函数入口地址,然后实现对应的行为。虚函数使代码更具有扩展性。

python的多态

子类继承父类,子类重写父类的函数,调用时就自动调用了子类的函数。

C++和python的区别

  1. C++是编译型语言,需要先编译再执行,编译后通常不可以跨平台。
  2. python是解释型语言,直接运行,跨平台好。上手快,容易浮于表面不够深入。

python带来的收益

快速上手机器学习和深度学习,直接调用大量的库函数。

STL底层容器和实现原理

  1. vector是动态数组,分配连续的内存,2倍扩容。
  2. list双向表,插入删除效率高。
  3. map和set红黑树,有序的容器。
  4. stack和queue底层可能都是数组实现
  5. unordered_map和unordered_set哈希表。
  6. array栈上分配的数组执行效率快。
  7. tuple元组多数据类型的集合。

快排

基于交换和分治的算法, 平均nlogn 最坏n平方

四次挥手

主动关闭方发送FIN标志位,自身状态进入FIN_WAIT_1被动关闭方收到FIN后,发送ACK确认,自身进入CLOSE_WAIT状态,当主动关闭方收到ACK后,进入FIN_WAIT_2状态,此时主动关闭方只能接收数据,不能发送数据,因为TCP是全双工的,所以要等待被动关闭方关闭后才结束。此时进行了两次挥手,双方进入半关闭状态。当被动关闭方需要关闭时,发送FIN标志位,发出后自身状态进入LAST_ACK状态,如果对方收到FIN标志位后,发送ACK应答,自身进入TIME_WAIT状态,等待2MSL后关闭连接。等待的2MSL是报文在网络传输中一个来回的长度。确保最后一个ACK能被对方收到。

static关键字

static可以修饰普通函数变量和类成员函数和变量。

  1. static修饰普通变量时,分为全局变量和局部变量,两者都保存在静态区,并且只初始化一次,在整个程序运行期间一直存在。全局变量和局部变量的区别是作用域不同。
  2. static修饰普通函数时,说明此函数只在本文件中可见,防止多个文件的同名冲突。
  3. static修饰类中的成员变量时,必须在类中声明,在类外初始化,初始化的时候分配内存,所有的static成员变量为所有对象共享。
  4. static修饰类中的成员函数时,只能调用static的变量和函数,没有this指针,所有对象共享这个函数,可以使用类名直接调用。

变量分为全局变量和局部变量,static修饰全局变量时,表示这个变量只在本文件中可见

const关键字

const用于限定变量指针和函数不可改变,方便编译器做类型检查。

  1. cons修饰变量时必须初始化。const全局变量通常放在静态区,const局部变量放在栈区。
  2. cosnt修饰成员函数时,函数中的成员变量不可更改,如果要修改成员变量需要声明为mutable
  3. const修饰指针有两种,常量指针和指针常量,常量指针是指针的指向的值不可改变,而指针的指向可以改变。指针常量是指向不可变,而值可变。

const和define的区别

  1. const明确指定类型,编译器对类型做检查,而define没有类型也不 检查。
  2. const分配内存,而define不分配。
  3. const在编译期处理,而define在预编译期进行宏替换。define的宏替换不加括号会产生严重的影响。

指针和引用的区别

  1. 指针保存的是所指对象的地址,而引用是所指对象的别名。指针通过解引用间接访问所指的对象,而引用直接访问。
  2. 指针可以有多级,而引用最多两级。当有两个取地址符时,是右值引用,右值引用可以减少深拷贝的次数。
  3. 指针定义时可以不初始化,即使初始化后也可以改变。而引用定义时必须初始化,初始化后不可以改变。
  4. 引用的本质是指针常量,编译器帮助转换。指针常量的指向不可以改变,值可以变。

define与内联函数的区别

  1. 内联函数是一个函数,在编译期插入到调用的地方,而define在预处理期进行替换。
  2. 内联函数避免了函数调用时的压栈和参数拷贝等操作,提高了性能。
  3. 内联函数对参数有类型检查。define不加括号容易出错。

new和malloc的区别

  1. malloc是库函数,new是运算符
  2. malloc只分配内存不初始化,而new不仅分配内存也初始化。new分配内存以后自动调用构造函数。
  3. malloc分配内存时必须指定内存大小,而new可以自动计算。malloc分配完成后返回的是void*类型,需要强转,而new返回的是对应类型的指针。
  4. malloc分配内存失败时返回NULL,而new分配内存失败时抛出bad_alloc异常。

http和https的区别

两者都是用于客户端和服务器端通信。

https是http的安全版。

主要区别在于:

  1. http是明文传输,https是密文传输。
  2. http默认端口是80, https的默认443
  3. https需要验证服务器端的身份,如果CA证书不正确则会中断通信。
  4. CA证书需要成本,加密解密的过程增加CPU和内存的开销。

https增加了ssl层,用于确保传输的安全性。

通信前先进行ssl层的握手,首先客户端ssl版本号和加密组件发送给服务器端。

服务器端筛选出可用的ssl版本号和加密算法同时加上CA证书发送给客户端。

客户端验证CA证书的有效性,如果无效则中断通信。

若有效客户端发送加密的pre-master secret随机密码串,这一步使用的是非对称加密,用于协商后面对称加密的密钥,所以这一步不能被篡改和截获。客户端得到服务器端的响应后,且验证通过后,后续就使用对称加密加密算法进行加密。然后进行TCP三次握手。

http1.0和http1.1的区别

http1.0只支持短连接,即一次通信完成后就立即断开

http1.1支持长连接,一次TCP建立以后,可以进行多次请求。

http1.0只提供了三种请求方法:get post head

http1.1增加了五种请求方法:put delete connect trace options

http1.1增加了许多状态码,比如100 continue 表示已经收到,等待后续的资源。206 partial content 部分资源

http1.1支持一个物理主机上可以有多个虚拟主机共用一个IP