百度C++研发工程师面经
Posted 范中豪
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了百度C++研发工程师面经相关的知识,希望对你有一定的参考价值。
本篇博文主要介绍2021秋招时汇总的一些百度后端面试过程中可能遇到的一些问题
C++ 内存分为几部分?介绍堆和栈的区别
text(代码段): 用来存放程序执行代码,同时也可能会包含一些常量(如一些字符串常量等)。该段内存为静态分配,只读(某些架构可能允许修改)
data(数据段):用来存放程序中已经初始化的非零全局变量,静态分配。data又可分为读写(RW)区域和只读(RO)区域,RO段保存常量所以也被称为.constdata,RW段则是普通非常全局变量,静态变量就在其中
bss:存放程序中未初始化的和零值全局变量。静态分配,在程序开始时通常会被清零。
堆:在内存开辟另一块存储区域,般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收
栈:程序运行时由编译器自动分配,存放函数的参数值,局部变量的值等
堆和栈的区别:
- 栈由系统自动分配和管理,堆由程序员手动分配和管理
- 栈由系统分配,速度快,不会有内存碎片
- 堆由程序员分配,速度较慢,可能由于操作不当产生内存碎片
- 栈从高地址向低地址进行扩展,堆由低地址向高地址进行扩展
- 程序局部变量是使用的栈空间,new/malloc 动态申请的内存是堆空间,函数调用时会进行形参和返回值的压栈出栈,也是用的栈空间
程序结束后如何回收内存?(析构函数)
在 main()函数中的显示代码执行之前,会调用一个由编译器生成的_main()函数,而_main()函数会进行所有全局对象的的构造及初始化工作。而在main()函数结束之前,会调用由编译器生成的exit函数,来释放所有的全局对象
假设我们要在main()函数执行之前做某些准备工作,那么我们可以将这些准备工作写到一个自定义的全局对象的构造函数中,这样,在main()函数的显式代码执行之前,这个全局对象的构造函数会被调用,执行预期的动作,这样就达到了我们的目的。
垃圾回收的时候,只需要扫描 bss 段, data 段以及当前被使用着的栈空间,找到可能是动态内存指针的量,把引用到的内存递归扫描就可以得到当前正在使用的所有动态内存了。
析构函数是否可以为虚函数?构造函数是否可以是虚函数
析构函数可以,构造函数不行
虚函数的调用需要虚函数表指针,而该指针存放在对象的内容空间中;若构造函数声明为虚函数,那么由于对象还未创建,还没有内存空间,更没有虚函数表地址用来调用虚函数——构造函数了。
New和malloc,delete和free
new 分配内存按照数据类型进行分配,malloc 分配内存按照指定的大小分配
new 返回的是指定对象的指针,而 malloc 返回的是 void*,因此 malloc 的返回值一般都需要进行类型转化
new 不仅分配一段内存,而且会调用构造函数,malloc 不会
new 分配的内存要用 delete 销毁,malloc 要用 free 来销毁;delete 销毁的时候会调用对象的析构函数,而 free 则不会
new 是一个操作符可以重载,malloc 是一个库函数
malloc 分配的内存不够的时候,可以用 realloc 扩容。new 没用这样操作
new 如果分配失败了会抛出 bad_malloc 的异常,而 malloc 失败了会返回 NULL
申请数组时: new[]一次分配所有内存,多次调用构造函数,搭配使用delete[],delete[]
多次调用析构函数,销毁数组中的每个对象。而 malloc 则只能 sizeof(int) * n
C++四种类型转换
- const_cast: 用于将 const 变量转为非 const
- static_cast: 用于各种隐式转换
- dynamic_cast: 用于动态类型转换。只能用于含有虚函数的类
- reinterpret_cast: 几乎什么都可以转
用过数据库中的哪些事务
数据库的索引底层的实现
B+树(多路平衡树)
查询时命中主键和普通值有什么区别
主键是聚簇索引
HTTP错误码有哪些了解么
事务回滚有什么实现机制?除了日志之外呢?
ublog
代码回滚
进程和线程区别?进程与线程共享什么资源?线程独占什么资源?
介绍 UDP 协议
TCP三次握手四次挥手
TCP重传
指针和引用的区别
- 指针是一个实体,需要分配内存空间。引用只是变量的别名,不需要分配内存空间
- 引用在定义的时候必须进行初始化,并且不能够改变。指针在定义的时候不一定要初始化,并且指向的空间可变
- 有多级指针,但是没有多级引用,只能有一级引用
- 指针和引用的自增运算结果不一样。(指针是指向下一个空间,引用时引用的变量值加 1)
- sizeof 引用得到的是所指向的变量(对象)的大小,而 sizeof 指针得到的是指针本身的大小
- 引用访问一个变量是直接访问,而指针访问一个变量是间接访问
- 使用指针前最好做类型检查,防止野指针的出现
- 使用指针前最好做类型检查,防止野指针的出现
- 作为参数时也不同,传指针的实质是传值,传递的值是指针的地址;传引用的实质是传地址,传递的是变量的地址
一致性hash及其用途和数据倾斜如何解决
详细
通过hash环来实现负载均衡,将不同的服务器hash映射到一致性hash环上,当服务请求到来时,使用hash将其映射到hash环上,然后可以采用如顺时针寻找的方法选择距其最近的服务器进行服务。
当服务器较少或hash公式不够好时,可能出现大多数请求都会落在同一个服务器上,这就是数据倾斜,可以采用添加服务器、虚拟节点、更换一致性hash的方法进行解决。
list 和 vector 的区别, 用什么方法可以提升链表的查询效率
- vector 和数组类似,拥有一段连续的内存空间,并且起始地址不变。因此能高效的进行随机存取
- list 是由双向链表实现的,因此内存空间是不连续的。只能通过指针访问数据,所以 list 的随机存取非常没有效率,时间复杂度为 o(n);但由于链表的特点,能高效地进行插入和删除。由于涉及对额外指针的维护,所以开销比较大
- 提升查询效率的方法: 详细 1. 跳表 2. 散列表+链表
有两张表:student(id,name),score(id,sid,score,course)假设所有学生都参与各科考试,没有任何缺考,查询出所有科目分数都大于80分的学生姓名
有2个大文件分布在两台机器上,每个文件1T,两个文件的diff大小1m,用最小的网络流量交换两个文件
Hash的实现方式有哪些
直接地址法
平方取中法
除留余数
浏览器打开一个网页经历了怎样的过程
dns解析,tcp链接,https连接中的ssl加密过程,https链接
详细
- 浏览器构建HTTP Request请求
- 网络传输
- 服务器构建HTTP Response 响应
- 网络传输
- 浏览器渲染页面
Hash冲突的解决方式
开放定址法: 当发生地址冲突时,按照某种方法继续探测哈希表中的其他存储单元,直到找到空位置为止
再哈希法:当发生哈希冲突时使用另一个哈希函数计算地址值,直到冲突不再发生
链地址法:将所有哈希值相同的 Key 通过链表存储。key 按顺序插入到链表中
建立公共溢出区:采用一个溢出表存储产生冲突的关键字。如果公共溢出区还产生冲突,再采用处理冲突方法处理
Stl map是怎么实现的,为什么用红黑树不用hash
红黑树,有序需求
Linux查看一个100G的文件该用什么命令
cookie和session的区别?session是存在服务器的什么地方,怎么存储的?
当用户首次与Web服务器建立连接的时候,服务器会给用户分发一个 SessionID作为标识。SessionID是一个由24个字符组成的随机字符串。用户每次提交页面,浏览器都会把这个SessionID包含在 HTTP头中提交给Web服务器,这样Web服务器就能区分当前请求页面的是哪一个客户端。这个SessionID就是保存在客户端的,属于客户端Session。
Cookie分为内存中Cookie(也可以说是进程中Cookie)和硬盘中Cookie。大部分的Session机制都使用进程中Cookie来保存Session id的,关闭浏览器后这个进程也就自动消失了,进程中的Cookie自然就消失了,那么Session id也跟着消失了,再次连接到服务器时也就无法找到原来的Session了。当然,我们可以在登陆时点击下次自动登录,比如说CSDN的“记住我一周”,或者我们的购物车信息可以在切换不同浏览器时依然可用。这就要用到我们上文提到的另一种Cookie了——硬盘中Cookie,这时Session id将长期保存在硬盘上的Cookie中,直到失效为止。
详细
两个1T文件如何找到公共部分
- 将两个文件的数据分别通过hash映射到小文件中,然后依次比较每个小文件数据是否相同
进程间有哪些通信方式?
- 管道
- FIFO 有名管道
- 消息队列
- 信号量
- 信号
- 共享内存
- socket
线程间通信方式
- 临界区
- 互斥对象
- 信号量
- 事件对象
C++ 智能指针
shared_ptr,unique_ptr,weak_ptr,auto_ptr
红黑树查询、插入和删除的时间复杂度分别是多少?
O(log n)
redis有几种数据类型
TCP和UDP的区别?能举一些常见的使用场景吗?有哪些使用他们的应用层协议?
https和http的区别是什么?
redis的缓存击穿是怎么出现的?有什么解决办法
https请求的完整过程
- 浏览器请求连接
- 服务器返回证书:证书里面包含了网站地址,加密公钥,以及证书的颁发机构等信息
- 浏览器收到证书后作以下工作
- 验证证书的合法性
- 生成随机(对称)密码,取出证书中提供的公钥对随机密码加密
- 将之前生成的加密随机密码等信息发送给网站
- 服务器收到消息后作以下的操作
- 使用自己的私钥解密浏览器用公钥加密后的消息,并验证 HASH 是否与浏览器发来的一致;获得浏览器发过来的对称秘钥
- 使用加密的随机对称密码加密一段消息,发送给浏览器
- 浏览器解密并计算握手消息的 HASH:如果与服务端发来的 HASH 一致,此时握手过程结束,之后进行通信
Linux用户态占用过高怎么排查
top, ps
给你一个包含100亿个url的文件,请你找出使用频率最高的10个url,应该怎么做?
用哈希分成多个文件,再把这些小文件一次性加到内存用map,取前10。最后从每个文件的前10
如果用户在检索的时候,会出现返回的链接点进去是无效的或非相关的内容,如何发现和解决这个问题?
网络中有上亿个url,每个url都有一个id唯一标识,现在给你100台机器,你会怎么去设计他们的缓存系统?
用哈希对流量分组,每台机器承接一定的流量,再搞点负载均衡的策略
我的本地机器只有2.5G,但我想申请4G的内存空间,可以做到吗?
取决于你的系统总线数量,如果是32位的,则最大的可寻址内存空间为4G,而如linux系统还要保留1G,则不能申请,如果是64位的则可以申请。
具体谈谈地址空间存在的意义以及实现的方法?
http 和 https 区别,https 在请求时额外的过程,https 是如何保证数据安全的
IP 地址子网划分
POST 和 GET 区别
- 发送机制不同,GET 一般用于查询/获取资源信息,而 POST 一般用于更新资源信息。
- GET 请求的数据会附在 URL 之后,POST 把提交的数据放置在 HTTP 请求体中
- GET 方式提交的数据最多只能是 1024 字节(取决于操作系统的支持),POST 理论上没有数据量的限制(取决于服务器的处理能力)
- GET 方式提交的数据最多只能是 1024 字节(取决于操作系统的支持),POST 理论上没有数据量的限制(取决于服务器的处理能力)
- GET 请求会被浏览器自动缓存,而 POST 不会,除非手动设置。在浏览器回退时,GET 是无害的,POST 会再次提交请求。GET 请求参数会被完整保留在浏览历史记录中,而 POST 中的参数不会被保留
- 在发送请求时,GET 产生一个 TCP 数据包,服务器响应 200。POST 产生两个 TCP 数据包,浏览器先发送 header,响应 100,再发送 data,响应 200
- GET 请求只能进行 url 编码,而 POST 支持多种编码方式
DNS 解析过程
- 浏览器先检查自身缓存中有没有被解析过这个域名对应的 ip 地址
- 如果浏览器缓存没有命中,浏览器会检查操作系统缓存中有没有对应的已解析过的结果。在 windows 中可通过 c 盘里 hosts 文件来设置
- 还没命中,请求本地域名服务器来解析这个域名,一般都会在本地域名服务器找到
- 本地域名服务器没有命中,则去根域名服务器请求解析
- 根域名服务器返回给本地域名服务器一个所查询域的主域名服务器
- 本地域名服务器向主域名服务器发送请求
- 接受请求的主域名服务器查找并返回这个域名对应的域名服务器的地址
- 域名服务器根据映射关系找到 ip 地址,返回给本地域名服务器
- 本地域名服务器缓存这个结果
- 本地域名服务器将该结果返回给用户
硬链接与软链接的区别
软链接可以看作是 Windows 中的快捷方式,可以让你快速链接到目标档案或目录。硬链接则透过文件系统的 inode 来产生新档名,而不是产生新档案
硬链接(hard link):A 是 B 的硬链接(A 和 B 都是文件名),则 A 的目录项中的 inode 节点号与 B 的目录项中的 inode 节点号相同,即一个 inode 节点对应两个不同的文件名,两个文件名指向同一个文件,A 和 B 对文件系统来说是完全平等的。如果删除了其中一个,对另外一个没有影响。每增加一个文件名,inode 节点上的链接数增加一,每删除一个对应的文件名,inode 节点上的链接数减一,直到为 0,inode 节点和对应的数据块被回收
软链接(soft link):A 是 B 的软链接(A 和 B 都是文件名),A 的目录项中的 inode 节点号与 B 的目录项中的 inode 节点号不相同,A 和 B 指向的是两个不同的 inode,继而指向两块不同的数据块。但是 A 的数据块中存放的只是 B 的路径名(可以根据这个找到 B 的目录项)。A 和 B 之间是“主从”关系,如果 B 被删除了,A 仍然存在(因为两个是不同的文件),但指向的是一个无效的链接
不能对目录创建硬链接;不能对不同的文件系统创建硬链接;不能对不存在的文件创建硬链接
可以对目录创建软连接;可以跨文件系统;可以对不存在的文件创建软连接
Kill用法,某个进程杀不掉的原因(进入内核态,忽略kill信号)
- SIGNKILL(9) 的效果是立即杀死进程. 该信号不能被阻塞, 处理和忽略。
- SIGNTERM(15) 的效果是正常退出进程,退出前可以被阻塞或回调处理。并且它是Linux缺省的程序中断信号(默认是15)。
- kill - 9 表示强制杀死该进程;与SIGTERM相比,这个信号不能被捕获或忽略,同时接收这个信号的进程在收到这个信号时不能执行任何清理
- 处于内核态的进程会屏蔽所有信号
- 僵死进程也不能被kill
系统管理命令(查看内存,网络情况)
- free查看内存
free -m --查看内存,不带单位
free -h --查看内存使用情况,带单位,显示查看结果
total:总计物理内存的大小
used:已使用内存
free:可用内存
Shared:多个进程共享的内存总额
- netstat查看网络情况
管道的使用
消息队列,怎么实现的
grep的使用
find和awk的使用
Linux 下的一些指令,$(进程 id),$?(上一条命令退出时状态)
怎么查看进程,按照内存大小,CPU 占用排序等等。(大写 M 和大写 P)
详细
介绍epoll
b 树和 b+ 树
进程间的通信,共享内存方式的优缺点
共享内存是进程间通信中最简单的方式之一。共享内存允许两个或更多进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针。当一个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改。
因为所有进程共享同一块内存,共享内存在各种进程间通信方式中具有最高的效率。访问共享内存区域和访问进程独有的内存区域一样快,并不需要通过系统调用或者其它需要切入内核的过程来完成。同时它也避免了对数据的各种不必要的复制。
因为系统内核没有对访问共享内存进行同步,您必须提供自己的同步措施。例如,在数据被写入之前不允许进程从共享内存中读取信息、不允许两个进程同时向同一个共享内存地址写入数据等。解决这些问题的常用方法是通过使用信号量进行同步。
共享内存块提供了在任意数量的进程之间进行高效双向通信的机制。每个使用者都可以读取写入数据,但是所有程序之间必须达成并遵守一定的协议,以防止诸如在读取信息之前覆写内存空间等竞争状态的出现。不幸的是,Linux无法严格保证提供对共享内存块的独占访问,甚至是在您通过使用IPC_PRIVATE创建新的共享内存块的时候也不能保证访问的独占性。 同时,多个使用共享内存块的进程之间必须协调使用同一个键值。
给你一个系统,后台的逻辑已经实现了,但是前端加载很慢,怎么检测
分布式缓存的一致性,服务器如何扩容(哈希环)
一致性hash
项目中遇到的困难(提前想好,并且把实现或者优化方法说清楚)
用过哪些git命令
事务的特性
acid
mysql索引,innodb和myisam的差别
唯一性索引或者开发分布式锁
redis
了解哪些设计模式
多个线程达到同一个状态然后再一起执行,达到某一个状态之后再继续并发执行,这种怎么实现?
读写锁中加读锁后如何避免写线程饿死?
如何实现控制线程在某段时间内完成,不完成就撤销
程序装载进内存后分别在哪里
代码在.init .text .rodata,初始化的全局变量在.data,未初始化的在.bss,还有堆和用户栈
程序在调用中发生了什么?返回时又发生了什么?
大概就是在用户栈中压入参数,返回地址,%ebp,接着跳转,开辟栈空间,保存需要保存的寄存器。返回时反过来从栈中进行恢复。
同步/异步 阻塞/非阻塞
多态有哪些?分别怎么实现的?
静态函数多态通过编译时不同的函数名来实现,不同的函数名是怎么组合出来的?
动态多态通过虚函数实现
虚函数表头指针属于类还是对象? 对象
虚函数表属于类还是对象? 类
虚函数表存在哪里? 静态数据区
静态变量和非静态变量有什么区别?分别存在什么地方
要分为静态全局变量,静态局部变量,非静态全局变量,非静态局部变量来答
异步处理的幂等性
幂等需要通过唯一的业务单号来保证。也就是说相同的业务单号,认为是同一笔业务。使用这个唯一的业务单号来确保,后面多次的相同的业务单号的处理逻辑和执行效果是一致的。
下面以支付为例,在不考虑并发的情况下,实现幂等很简单:1. 先查询一下订单是否已经支付过,2. 如果已经支付过,则返回支付成功;如果没有支付,进行支付流程,修改订单状态为‘已支付’。
volatile
加密算法
MD5 -- 对称加密算法
RSA -- 非对称加密算法
索引数据结构
B+树、hash索引,bitMap位图索引
可以用两个epoll监听同一个描述符吗,有事件发生时,怎么工作
segment fault怎么用gdb调试,两个函数循环调用会怎样
详细
g++ -g 指明在编译的时候,产生调试信息
编译完成后输入使用gdb进入调试模式
run编译的程序会产生段错误,输入bt跟踪段错误
使用frame *参看具体出错的代码
listen的两个参数分别是什么
连接的socket以及队列长度
send、recv都是阻塞的,怎么控制在一定时间内完成send、recv,如果超时就不执行,怎么跟select联系起来
socket关闭时,两端的状态变化
四次挥手
epoll底层使用了红黑树,说一下红黑树的特点和优点
epoll内核中维护了一个内核事件表,它是将所有的文件描述符全部都存放在内核中,系统去检测有事件发生的时候触发回调,当你要添加新的文件描述符的时候也是调用epoll_ctl函数使用。
现在要在内核中长久的维护一个数据结构来存放文件描述符,并且时常会有插入,查找和删除的操作发生,这对内核的效率会产生不小的影响,因此需要一种插入,查找和删除效率都不错的数据结构来存放这些文件描述符,那么红黑树当然是不二的人选
printf的可变参数是怎么实现的,如果参数个数不匹配会发生什么,比如字符串需要3个参数,但是只传了2个或者4个分别会发生什么
函数调用栈里面存储的是什么
函数调用时:
主函数的下一条指令的地址入栈
函数的参数入栈,从右往左入栈
函数的局部变量入栈。注意:静态变量不入栈。
free怎么释放
使用指针,怎么尽量避免segment fault
token口令的实现原理,产生口令的客户端和服务端是否需要通信,具体实现接口(输入和输出)
gdb调试core文件
5种I/O模型介绍一下
- 阻塞IO
- 非阻塞IO
- IO复用
- 异步IO
- 信号驱动IO
shared_ptr的实现
UDP数据包长度多少
数据链路中数据帧内容最大长度为1500,IP头部需要20字节,UDP头部8字节,所以MTU为1472,TCP首部20字节
进程的状态有几种
数据库少了某些字段,现在要让你加,你怎么办?设计上有没有考虑可扩展性?
cookie有存什么东西吗
Linux静态库和动态库有什么区别?动态库的加载器是哪个
glibc是干什么的
glibc是GNU发布的libc库,即c运行库。glibc是linux系统中最底层的api
select什么时候比epoll好
少连接,高并发。连接少意味着不会超过select 1024的上限,高并发意味着一次wait每一个连接都会来数据
线程池怎么实现
平面里画出9个点10条边怎么画
分布式数据如何保证一致性
TCP四次挥手断开连接,为什么主动断开连接的那一方要有TIME_WAIT状态
Linux中查看端口、查找某个进程ID分别使用哪个命令
netstat, top
SQL语句中,order by 会用到索引吗
数据库索引覆盖问题,如果在修改数据时不按照索引的顺序,会怎样
Linux如何去查找某个文件,找出文件中的第10至20行
find
head -n 20 file | tail -n 10
百度有post请求吗
算法题
循环队列实现
旋转数组找最小值
跳台阶的题,每次跳一步或两步,有n级台阶,有多少种跳法
class Solution
public:
int numWays(int n)
if(n <= 1) return 1;
if(n == 2) return 2;
vector<long long> vec(n+1, 1);
vec[2] = 2;
for(int i = 3;i <= n;++i) vec[i] = (vec[i-1] + vec[i-2])%1000000007;
return vec[n];
;
给一个字符串,找出最长不重复子串
class Solution
public:
int lengthOfLongestSubstring(string s)
if(s.size() <= 1) return s.size();
unordered_map<char, bool> m;
int max_len = 1, i = 0, j = 1;
while(j < s.size())
m[s[i]] = true;
if(!m[s[j]])
m[s[j]] = true;
++j;
if(j-i > max_len) max_len = j-i;
else
m[s[i]] = false;
++i;
j = max(j, i+1);
return max_len;
;
给一个n*m的矩阵,0表示是水,1表示是陆地,求整个空间内有几块分离的岛屿
dfs
前序、中序和后序遍历
用加减乘除四种运算求根号二的值,保留小数点后10位
两个链表找公共节点
给一个函数,返回 0 和 1,概率为 p 和 1-p,请你实现一个函数,使得返回 0 1 概率一样
#include<stdlib.h>
#include<iostream>
#include<time.h>
int main()
srand(time(NULL));
int one_num = 0, zero_num = 0;
for(int i = 0;i < 10000;++i)
if(rand() % 10 >= 5) ++one_num;
else ++zero_num;
cout << "num of one: " << one_num << endl;
cout << "num of zero: " << zero_num << endl;
return 0;
如何理解IO复用?poll,select,epoll有什么区别
把一个 bst 转化成一个双向链表
手写一个全排列
写个 strcpy 函数
英文语句倒序输出
1亿个数中找1000个最大的
八个字母共有多少组合
一个有序数组,找出两个值加起来等于key值
找出1到n中重复的数字
合并两个排序链表
100G大文件排序,桶排序?
如何判断链表是否有环
最长公共子串
僵尸吃人问题,僵尸吃人后变成人,一个人能够被两个僵尸吃,人被吃后牺牲。问最后多少人存活
1-100以内的素数
以上是关于百度C++研发工程师面经的主要内容,如果未能解决你的问题,请参考以下文章