C++后端开发部分面试题(深信服、百度、腾讯等)

1. 内存分区:

	1. C++一般将内存分为五个存储区,分别是:栈区、堆区、自由存储区、静态存储区/全局存储区、常量存储区
	2. 栈:由编译器进行分配,在需要的时候分配,不需要的时候释放。一般用来存储局部变量或者函数参数等 2M

	1. 堆:由new关键字进行分配,配合delete进行释放。由于是交给程序员去管理,因此可能会出现内存泄漏。因此C++11出现了智能指针。因此new所能申请到的最大内存就是2G
	2. 自由存储区:一般是交给malloc进行分配的区域  配合free释放
	3. 静态存储区/全局存储区:用于存储静态变量,默认初始化为0 只初始化一次;全局变量
	4. 常量存储区:存储常量的,由操作系统进行管理
2. 栈比堆高效的原因

	1. 栈空间是由操作系统进行直接管理的 因此效率比较高
	2. 堆是由程序员进行管理的,很容易产生内存碎片,或者内存泄漏
3. 函数调用栈的过程


1. UDP中能不能用connect

	1. 可以用connect
	
2. 结构体能用memcmp吗?为什么

	1. 可以使用
	2. 要求两个结构体相同的结构
	3. 而且要在赋值前进行清零操作,否则结果不准确
3. malloc(0)产生的是什么结果? 

	1. 不论输入值的大小为多少,在malloc的内部最小的内存分配大小是一个定值(一般是8B),因为malloc需要用这部分空间来维护堆上的内存块链表。
	2. 所以当用户申请一块0B的空间时,malloc实际分配的空间是8B,如果用户申请的空间是X,则malloc实际分配的空间是(对齐(X)   +   8)。这也是为什么malloc分配的空间千万不能越界使用的原因:堆的内部链表结构将被破坏。
4. 访问一个网址的流程

	1. 浏览器通过搜索DNS缓存表(浏览器自己的DNS缓存表 也包括服务器的缓存表),将URL转换成对应的ip地址
	2. 根据ip地址和服务器建立连接(三次握手 客户端发送一个syn包 将自己置于syn-send状态,当服务器同意连接后悔回复一个ack+syn包 并将自己置于syn-recv状态。 客户端收到服务器回复后,如果它仍然需要进行连接,那么它就会回复一个ack包,最后将二者都置于istbles状态)
	3. 浏览器和服务器通信:浏览器请求,服务器处理请求

		1. 浏览器根据URL会生成HTTP请求,请求中包含文件的位置,请求文件的方式等
		2. 服务器连接到请求后,会根据http请求中的内容来决定如何获取相应的html文件
		3. 服务器将得到的HTML文件发送给浏览器
		4. 浏览器接收到部分HTML后便开始渲染网页
	4. 浏览器和服务器断开连接(断开连接的过程是一个四次挥手的过程)

		1. 主动方可能是浏览器也可能是服务器
		2. 当主动方发送一个fin包表明自己要断开连接,这说明主动方已经完成了自己的发送任务,被动方同意它断开连接会回复一个ack确认包
		3. 被动方此时如果还有未发送完成的数据就会在此时发送给对方,当完成后会发送一个fin断开连接请求的包
		4. 主动方收到后同意断开就回复一个ack包。至此,一个和平友爱的拆包过程就完成了
5. 标准IO和文件IO
	1. 文件I/O称之为不带缓存的IO(unbuffered I/O)。不带缓存指的是每个read,write都调用内核中的一个系统调用。也就是一般所说的低级I/O——操作系统提供的基本IO服务,与os绑定,特定于linix或unix平台。
	2. 标准I/O是ANSI C建立的一个标准I/O模型,是一个标准函数包和stdio.h头文件中的定义,具有一定的可移植性。标准I/O库处理很多细节。例如缓存分配,以优化长度执行I/O等。标准的I/O提供了三种类型的缓存。
6. socket同步与异步

	1. 同步: 所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。也就是必须一件一件事做,等前一件做完了才能做下一件事。
	2. 异步: 异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。重叠IO的接收数据的过程就是一个异步处理的过程
	3. 阻塞: 阻塞调用是指调用结果返回之前,当前线程会被挂起(线程进入非可执行状态,在这个状态下,cpu不会给线程分配时间片,即线程暂停运行)。函数只有在得到结果之后才会返回。
	4. 非阻塞: 非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。
7. hash扩容

	1. 哈希表有一个哈希因子(已存数据数/可存数据数)当哈希因子小于1表明可以继续存储
	2. 当哈希因子大于1之后,就要进行扩容
	3. 一般是借鉴vector经验 二倍扩容  然后在进行一次哈希函数将每一个键值对存储(%新的哈希表大小 得到每一个位置 重新分配)扩容的复杂度是O(N)  但是哈希函数由托管机制  也就是会在线下扩容 然后将数据上传  所以扩容对性能并会有很大的影响
8. TCP 如何解决沾包

	1. 沾包产生的原因:

		1. 因为TCP有滑动窗口机制,窗口大小固定 但是如果发送的数据大于TCP发送缓冲区的大小,那么数据就会被切断,所以发送过去之后粘结在一起的数据  
		2. 因为数据粘结在一起,因此解析的时候可能就会发生错误  这就发生了沾包问题
	2. 解决沾包

		1. 为发送的每一个包添加首部,首部应该包含数据包的长度,解析的时候只解析满足条件的数据包
		2. 发送端将每个数据包封装为固定大小,不够的大小通过0来填充
9. 除了TCP/UDP还知道哪些传输协议

	1. FTP文件传输协议
	2. http超文本传输协议  以及安全的HTTPS协议
10. 网络序和主机序

	1. 计算机正常的内存增长方式是从低到高(当然栈不是),取数据方式是从基址根据偏移找到他们的位置



	1. 网络序是大端存储, 大端存储因为第一个字节就是高位,从而很容易知道它是正数还是负数,对于一些数值判断会很迅速
	2. 主机序是小端存储,也就是低地址存高位。 第一个字节是它的低位,符号位在最后一个字节,这样在做数值四则运算时从低位每次取出相应字节运算,最后直到高位,并且最终把符号位刷新,这样的运算方式会更高效。
1. 如何进行多线程的调试

	1. gdb 是Unix下用来调试C和C++程序的常用的调试器. 它使你能在程序运行时观察程序的内部结构和内存的使用情况。但在多线程编程过程中很多程序问题出在启动阶段,而且很难使用gdb进行调试,我们可以采用手工插入以下辅助代码暂停程序运行
	2. 也可以通过打印log日志的方式进行调试

		1. log函数要短小精悍
		2. log的副作用:可能导致资源的消耗 降低程序的效率
2. 怎么判断一个数是否是2的n次方  如果一个数是2的N次方,那么它的二进制只有一个1
3. 缺页中断:

	1. 在请求分页系统中,可以通过查询页表中的状态位来确定所要访问的页面是否存在于内存中。
	2. 每当所要访问的页面不在内存时,会产生一次缺页中断,此时操作系统会根据页表中的外存地址在外存中找到所缺的一页,将其调入内存。
	3. 当发生缺页中断后,需要经过如下步骤的处理:
	4. 
4. 三次握手(如果第三步的ack包丢失了) 服务器回复一个ack和syn包后,就会开启一个定时器,如果超过时间后未能接收到数据连接,那么就会重传
5. 在一个大项目中怎么定位cpu占用过高的代码段?  可以使用visual studio 的性能审查工具
6. 数据库的内连接、外连接、左连接和右连接:

	1. 两表交叉,只有两个表都有的才会被显示出来
	2. 外连接也叫全连接,查询出两个表的所有数据,但是要去掉重复数据
	3. 左连接,左表为主,右表在左表中有的数据才会被显示出来
	4. 右连接,右表为主,左表在右表中有的数据才会被显示出来
7. 二分查找和优化

	1. 普通二分每次中间位置的计算都是通过 middle = (low + high) / 2;
	2. 这样普通的二分会出现一个问题就是,如果low和high数据值都比较大的时候,加在一起可能会溢出  因此需要使用low+(high-low)/2直接进行计算 
	3. 但是对于要查找的数据在数组的某一端的时候,就会出现很多次的无效划分和查找
	4. 因此需要根据比例划分数据   说白了就是用这个数在数组的某一端进行比例划分
	5. 
8. 栈排序  一个数据栈和一个空栈(辅助栈) 实现数据的排序

	1. 先将数据栈的栈顶元素放到辅助栈中,然后用数据栈的元素和辅助栈的栈底元素(help.peek()) 比较
	2. 只有大于栈底的元素才能压入help
	3. 否则那么就要把help的数据弹出并写回到数据栈,一直维护栈底元素为最小
	4. 最后当数据栈空的时候,将辅助栈的数据写回到数据栈,这就得到了一个从小到大的数据栈
9. 深拷贝和浅拷贝

	1. 深拷贝和浅拷贝的最大区别在于,深拷贝有空间的开辟,而浅拷贝只是进行简单的赋值操作
	2. 如果一个对象拥有资源,当它要进行复制的时候就是深拷贝,否则只需要浅拷贝
	3. 出现的意义,如果一个对象拥有资源,当你通过它去初始化另一个对象的时候,如果只是简单的进行赋值而没有内存的申请,那么在对象生命周期结束的时候 调用析构函数 释放内存就会发生内存泄漏(释放一块不存在的内存)
10. 拷贝构造函数的调用时机

	1. 当一个对象通过另一个对象进行初始化的时候
	2. 对象以值传递的方式作为参数传入
	3. 对象以值传递的方式从函数返回
11. 项目上遇到的最大问题,如何解决

	1. 对于斗地主这个项目,遇到的最大问题就是在实现玩家离开智能托管的时候,如何智能的做出最优决策,而不是简单的智能提示牌型
	2. 要实现这个功能,就需要在每次出牌之前分析手牌,并使用动态规划的思想实现,分析出如果采用当前出牌策略最终会剩下几张牌
	3. 由最后剩下的无法组成任何牌型的牌数构建出牌组合的优先级
	4. 由这个分析出的优先级决定如何去走当前的这一步牌
12. http和https的区别

	1. http是超文本传输协议,作用在osi的应用层。用于从www服务器传输数据到本地浏览器,使传输更高效
	2. https是http+ssl  更加注重用户和服务器的安全性
	3. 主要区别:

		1. https需要申请ca证书
		2. http发送的数据时明文,而https发送的数据是加密的数据
		3. http的端口号是80 https的端口号是443
		4. http是无状态的,很多时候借助cookies和session来表明用户和会话状态
		5. https则能够进行身份验证、信息加密等操作  因此https不仅加重了经济压力同时也加重了服务器的压力
13. tcp协议有哪些计时器,分别是做什么的

	1. 超时重传计时器

		1. 目的是为了防止报文段的丢失
		2. 当tcp发送报文段时,会创建重传计时器。
		3. 在计时器截止之前收到了报文段的确认信息,那么就撤销计时器,否则重传报文 
		4. 重传时间一般是 2*RTT
	2. 坚持计时器

		1. 目的是解决零窗口大小通知可能导致的死锁
		2. 如果接收端此时没有空余位置接收数据了,就会向发送端发送一个零窗口报文信息,通知发送端,暂时停止数据的发送
		3. 当接收端有了空余空间之后会发送消息通知发送端说我现在有空间可以接收数据了,你可以准备发送了。但是这个过程很可能会出现问题就是,接收端发送的数据丢失了,那么接收端以为自己发送成功了,就一直等发送端发来数据。可是发送端并没有收到发送消息的通知。
		4. 至此就形成了,接收端一直等待数据发送,而发送端一直等对方通知可以发送数据
		5. 为了解决这个问题,tcp为每一次连接都设置了一个坚持计时器
		6. 原理就是,当发送端收到了接收端零窗口的通知后就会开启一个坚持计时器。当坚持计时器时间截止之后,接收端仍未发来可以发送数据的通知的话,那么发送端就会发送一个探测报文段,这个探测报文段一般只有一个字节,目的在于通知接收端说,你的请求消息报文已经丢失,需要你重写发送一下。
		7. 如果还未收到接收端的请求数据消息,他会加倍当前坚持计时器的时间,如果超过60s,则会每60s就发送一个探测报文,知道窗口重新打开
	3. 保活计时器

		1. 目的在于防止tcp连接的客户端和服务器出现长期无效的连接“连接 但没有数据的交换”
		2. 在服务器接收到客户端的数据之后会重置保活计时器,一般为2h  当超过两个小时都没有数据往来,那么服务器会连续十次的每隔75s向客户端发送探测报文,如果十次后都没有数据发送过来,那么服务器就会断开二者的连接
	4. 时间等待计时器

		1. 目的是为解决tcp断开连接的过程有重复的fin包
		2. 当断开连接请求发送后,但是还未收到对方的确认之间,这一时期称为过度时期  时间等待计时器就是在这期间发挥作用的
		3. 设置计时器时间为报文段期望生命周期的二倍,当到达终点后收到重复的fin包都将被丢弃
	5. 2MSL(2倍报文生命周期)计时器

		1. 本质上也就是时间等待计时器
		2. 四次挥手的过程中,当被动方发送fin包申请断开连接,主动方回应ack包 但是还未生效的这个过程里,就处于TIME_WAITE 状态
		3. 如果主动方的第四次ack包 在超时后都没有到达被动方,那么被动方就会重新发送fin包给主动方,主动方又会回应一个ack包。这样就会导致有很多重复的数据包到达终点,因此它会使连接进入一个2MSL状态,在这个状态中,每一个端口都无法使用,而且在这一个过程中收到的所有重复的报文都将会被丢弃。
		4. 目的在于 使得丢失的ack能够最大可能的被发送出去
		5. 这个计时器一般出现在主动发送方,因为只需要一方有就可以
14. redis是通过什么方式实现持久化的:RDB AOP
15. https中get和post的区别:

	1. post传输的数据量比较大,可以达到2M,而get方法由于受到URL长度的限制,只能传输1024字节
	2. get请求时,参数在URL中显示,而使用post请求,则不会显示出来
	3. get请求需要注意缓存问题,post请求不需要担心这个问题
	4. 发送请求时,因为get请求的参数都在URL里,所以send函数发送的参数为null,而post请求在使用send方法时,却赋予其参数
	5. get请求的数据会被浏览器缓存起来,因此其他人就可以从浏览器的历史记录中读取到这些数据。比如账号密码等。
	6. 某些情况下get会带来严重的安全问题,而post可以避免这些问题
16. 虚拟内存分配有两种形式,brk和mmap

	1. 当malloc小于128k的内存时,使用brk分配内存,将堆顶位置向高地址推  edata指针是指向数据段的最高地址,也是堆顶指针
	2. 当malloc大于128k的内存时,使用mmap分配,会在堆和栈之间的空闲区直接进行分配
	3. 为什么会采取这样的分配方式:

		1. 因为brk分配的内存只有在高地址上的内存释放后,才能指针回退。所以如果有低地址的空间被释放后就会出现内存碎片
		2. 但是mmap可以单独申请单独释放,不涉及内存碎片
		3. 需要注意的是,虽然有内存碎片产生,但是如果此时申请的内存大小和碎片大小一致,则会优先从链表中将碎片分配出去
		4. 如果内存碎片大小达到128k之后,会出现brk的紧缩操作。
17. 段错误:程序访问的内存超出了系统分配给程序的内存空间
18. 页错误:缺页中断:访问的页面不在内存中  处理方式:opt  lru(哈希表和双向链表)
19. 有进程了为什么还要线程

	1. 能够使系统在同一时间完成多件事情
	2. 进程如果在遇到系统IO的时候会被阻塞,而线程能够不依赖输入数据而继续执行工作
	3. 可以有效的利用多核处理器和计算机,在线程出现之前,多核处理器不一定能使进程执行速度变快
20. 从低到高的为了实现并发,针对指令集采用流水线,然后是线程  然后是进程
21. hash表的底层实现:

	1. 因为数组能够实现数据的随机读取,但是存储空间要求是连续的,所以插入和删除时间复杂度比较高;链表能够实现高效的插入和删除,但是读取比较麻烦。
	2. 哈希表就是在结合了这二者的优点后实现的一种新的数据结构
	3. 存储数据:

		1. 通过哈希函数(数字分析  平方取中、除留余数法、折叠法)由key作为参数计算得到哈希值,然后在数组中找到对应的位置,存储在其中
		2. 因为有大量的数据输入,所以很可能得到相同的下标值,所以这就出现了哈希冲突,因此解决哈希冲突的方案有:

			1. 开放定址法(线性探测再散列 二次探测  伪随机探测)
			2. 拉链法(再这个位置上建立链表  随后出现在这个位置上的值都放到链表里  为了防止链表过长造成数据读取时候复杂度过大,程序会主动控制链表的长度)
			3. 再哈希
			4. 建立公共溢出区
	4. 数据读取:

		1. 和存储是差别不大的
		2. 通过key然后由哈希函数 计算得到哈希值 也就是下标值  然后直接读取到相应位置上的value
22. 哈希表的哈希函数的特性:

	1. 不同的输入可能对应相同的输出
	2. 相同的输入一定对应相同的输出
	3. 无限的输入对应有限的输出
	4. 分布均匀
23. strcpy memcpy  memset 之间的区别

	1. strcpy是用来将一个字符串拷贝给另一个字符串的,第二个参数需要注意const避免被修改,返回值是char*  不需要指定长度  遇到/0会自动结束
	2. memcpy  是用来将一个内存拷贝给另一块内存的,能够拷贝的类型包括 数组、整型、结构体  类等  需要指定长度
	3. 用于对某一个结构进行初始化 为某一个相同的值
char* String::Strcpy(char* str, const char* src){
      if (str == nullptr || src == nullptr) return nullptr;
      char* t = str;
      while (*t++ = *src++);
      return str;
}


void  *memcpy(void *pvTo, const void *pvFrom, size_t size) {
       assert((pvTo != NULL) && (pvFrom != NULL));      // 使用断言
       byte *pbTo = (byte *) pvTo;         // 防止改变pvTo的地址
       byte *pbFrom = (byte *) pvFrom; // 防止改变pvFrom的地址
       while(size -- > 0 )
              *pbTo = *pbFrom ;
       return pvTo;
}
  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值