八股文
自我介绍
您好,我叫张龙涛。河南驻马店人,22 届新疆大学本科生。应聘的是研发工程师,非常感谢贵公司能给我这次机会。接下来我会从三个方面进行自我介绍:
研发工程师:(C/C++ 方向)
开发工程师
测试开发:
您好,我叫张龙涛。河南驻马店人,22 届新疆大学本科生。应聘的是测试开发工程师,非常感谢贵公司能给我这次机会。接下来我会从三个方面进行自我介绍:
你为什么能够胜任这个岗位
开发能力得到了锻炼
我相信自己的学习能力,可以快速上手
沟通能力,很有耐心,有责任感,我认为这在测试中也是很重要的一些素质。
对转转有什么认识:
转转由腾讯与58集团共同投资二手交易平台,2021 年 6 月,转转集团完成了D1轮融资。国内二手电商领域唯一一家短期内获得两轮大规模现金融资的公司。估值超18亿美元
微信点赞功能:
功能测试:
是否可以点赞
取消点赞
多次点赞会出现什么情况
多人点赞时的顺序是否按照时间顺序进行排列
点赞是否显示头像和名称
点赞之后能否进行评论
点赞之后退出该页面,再次进入朋友圈点赞消息是否还存在
多用户点赞,再次打开朋友圈是是否可以按照顺序看到是谁谁谁赞了我
接口测试:
点赞之后相同好友是否收到提示信息
相同好友处的提示信息是否按照时间顺序
相同好友处的点赞是否显示头像和名称
兼容测试:
电脑端和手机端是否都可以进行点赞和取消点赞功能
不同的移动端是否都可以行点赞和取消点赞功能(包括苹果,安卓)
可用性测试:
弱网的时候进行点赞是什么情况
网络断开时是否可以点赞
用户点击点赞几秒后可以看到点赞成功,取消同理
多用户同时给我点赞时,我是否可以全部接收到提示消息
安全性测试:
点赞是否会泄漏微信用户相关信息
有什么优点?
我学习能力比较强。比如说我中考的时候只考了二百多分,但是高考考了五百六十多。所以我人为我学习能力比较强
我对技术有中孜孜不倦的精神。大一上学我为了学习操作系统,先后看了汇编语言、操作系统真相还原、一个六十四位操作系统的实现等书籍,并在一个学期内完成了相关的内容
有什么缺点:
在我决定投递贵公司时,我还没有 UE 的相关经验。但是这两天我一直在抽空学习 UE 相关的内容,而且也自己手动对 UE 源码进行了编译。接下来我会买相关的书籍更加系统地学习相关的内容,希望在进入实习期间时能够较为独立地完成项目
内存模型
在 C++ 中,虚拟内存分为代码段、数据段、BSS 段、堆区、栈区、文件映射区六部分。
代码段: 包括只读存储区和文本区,其中只读存储区存储字符串常量,文本区存储程序的机器代码。
数据段:存储程序中已初始化的全局变量和静态变量
BSS 段:存储未初始化的全局变量和静态变量(局部 + 全局),以及所有被初始化为 0 的全局变量和静态变量。
堆区:调用 new/malloc 函数时在堆区动态分配内存,同时需要调用 delete/free 来手动释放申请的内存。
栈:使用栈空间存储函数的返回地址、参数、局部变量、返回值
映射区: 存储动态链接库以及调用 mmap 函数进行的文件映射
进程和线程的区别
一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程依赖于进程而存在。
进程之间的资源是相互隔离的。线程之间共享进程的资源(除了线程的栈)
进程是资源分配的最小单位,线程是 CPU 调度的最小单位
进程的创建、撤销和切换的开销要高于线程
线程之间可以通过全局变量的方式进行通信,更加简单
进程调试简单;线程调试复杂。
进程间不会相互影响 ;线程挂掉将导致整个进程挂掉
进程适应于多核、多机分布;线程适用于多核
进程和线程的选择
需要频繁创建的优先使用线程
IO 繁忙的优先使用进程,计算繁忙的优先使用线程
需要分布式的时候使用进程
需要稳定性的时候使用进程
虚拟内存
扩大地址空间
内存保护:每个进程运行在各自的虚拟内存地址空间,互相不能干扰对方。虚存还对特定的内存地址提供写保护,可以防止代码或数据被恶意篡改。
内存公平分配:采用了虚存之后,每个进程都相当于有同样大小的虚存空间。
共享代码:当不同的进程使用同样的代码时,物理内存中可以只存储一份这样的代码,不同的进程只需要把自己的虚拟内存映射过去就可以了
当进程通信时,可采用虚存共享的方式实现
许多程序的片段可以同时保存在内存中。当一个程序等待它的一部分读入内存时,可以把 CPU 交给另一个进程使用。在内存中可以保留多个进程,提高了系统的并发性
在程序需要分配连续的内存空间的时候,只需要在虚拟内存空间分配连续空间,而不需要实际物理内存的连续空间,提高了内存的利用率
内存调度算法
OPT、FIFO、LFU、LRU、LRU-K、2Q、CLOCK
IO 模型
阻塞 IO:当用户线程发出 IO 请求之后,内核会去查看数据是否就绪,如果没有就绪就会等待数据就绪,而用户线程就会处于阻塞状态,用户线程交出 CPU。当数据就绪之后,内核会将数据拷贝到用户线程,并返回结果给用户线程,用户线程才解除 block 状态。
非阻塞 IO:当用户线程发起一个 read 操作后,并不需要等待,而是马上就得到了一个结果。如果结果是一个 error 时,它就知道数据还没有准备好,于是它可以再次发送 read 操作。一旦内核中的数据准备好了,并且又再次收到了用户线程的请求,那么它马上就将数据拷贝到了用户线程,然后返回。非阻塞 IO 需要使用轮询的方式查看数据是否准备好
信号 IO:用户线程发起一个 IO 请求操作,会给对应的 socket 注册一个信号函数,然后用户线程会继续执行,当内核数据就绪时会发送一个信号给用户线程,用户线程接收到信号之后,便在信号函数中调用 IO 读写操作来进行实际的 IO 请求操作。
异步 IO:当用户线程发起 read 操作之后可以直接去处理其它事件,而内核对 IO 进行处理,并在结束后发给进程一个信号
IO 多路复用:一个线程可以管理多个套接字的状态,通过轮询的方式来检测是否有事件到达,并且对到达的事件逐一进行响应
另外有两个高性能 IO 模型:
Reactor:客户向线程注册事件,然后线程轮询是否有事件发生
Proactor:当检测到有事件发生时,会新起一个异步操作,然后交由内核线程去处理,当内核线程完成 IO 操作之后,发送一个通知告知操作已完成。异步 IO 模型采用的就是 Proactor 模式
IPC
共享内存、信号、信号量、套接字、管道、消息队列、DBus
TCP 和 UDP
UDP 功能及其有限,只有复用分用和差错校验两个功能。而 TCP 还提供了流量控制、拥塞控制、数据重传等功能
UDP 是无连接的,TCP 在连接前必须经过三次握手
UDP 尽最大努力传输,TCP 则保证按时有序的到达
UDP 对报文不拆分、不合并,保留上面传下来的报文边界。TCP 的报文长度根据接受方窗口的大小确定
UDP 首部只有 8 字节。TCP 有 20 个字节
UDP 支持多点传输,TCP 是点对点传输
端口复用就是指 UDP 可以将数据发给不同的端口。分用就是多个端口可以通过 UDP 传输信息
TCP 如何保证数据可靠性
序列号、确认应答、超时重传
窗口控制与快速重传
拥塞控制和流量控制
三次握手和四次挥手
为什么是三次握手
TCP 是全双工通信,两次握手只能确定单向数据链路是可以通信的,并不能保证反向的通信正常
TCP 本来是和挥手一样四次,但是因为中间两次可以合并,说就变成了三次
为什么是四次挥手
因为客户端不想发数据的时候需要发送 FIN 信号并经过 ACK 确认。但是这时候服务段可能还需要发送数据,当服务段也发送 FIN 并得到 ACK 时才能保证链接已经断开
流量控制
流量控制就是让发送方的发送速率不要太快,要让接收方来得及接收,是点到点的问题
发送窗口每收到一个 ACK 就将 cwnd 大小翻倍
拥塞控制
防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载
慢开始、拥塞避免、快速重传
GET 和 POST
1、概括
对于 GET 方式的请求,浏览器会把 http header 和 data 一并发送出去,服务器响应 200(返回数据);
而对于 POST,浏览器先发送 header,服务器响应 100 continue,浏览器再发送 data,服务器响应 200 ok(返回数据)
2、区别:
GET 参数通过 url 传递,POST 放在 request body 中。
GET 请求在 url 中传递的参数是有长度限制的,而 POST 没有。
GET 比 POST 更不安全,因为参数直接暴露在 url 中,所以不能用来传递敏感信息。
GET 请求只能进行 url 编码,而 POST 支持多种编码方式。
GET 请求会浏览器主动 cache,而 POST 支持多种编码方式。
GET 请求参数会被完整保留在浏览历史记录里,而 POST 中的参数不会被保留。
GET 和 POST 本质上就是 TCP 链接,并无差别。但是由于 HTTP 的规定和浏览器 / 服务器的限制,导致他们在应用过程中体现出一些不同。
GET 产生一个 TCP 数据包;POST 产生两个 TCP 数据包。
如何设计数据库
需求分析:了解用户的数据需求、处理需求、安全性及完整性要求;
概念设计:通过数据抽象,设计系统概念模型,一般为 E-R 模型;
逻辑结构设计:设计系统的模式和外模式,对于关系模型主要是基本表和视图;
物理结构设计:设计数据的存储结构和存取方法,如索引的设计;
系统实施:组织数据入库、编制应用程序、试运行;
运行维护:系统投入运行,长期的维护工作。
三个范式
1NF:数据表中的所有码都是不可再分的 2NF:存在唯一一个主键,且其它关键字与主键一一对应 3NF:非主属性不依赖于非主键属性
数据库隔离级别
隔离级别 |
作用 |
|---|---|
Read Uncommitted |
什么也不做 |
Read Committed |
事务提交后才能读取数据,解决幻读 |
Repeated Read |
在一个事务中,对同一份数据的读取结果总是相同的,解决脏读、解决重复读 |
Serialization |
事务串行执行。通过牺牲系统的并发性解决并发事务的所有问题 |
设计模式
单例模式:解决一个全局使用的类频繁的创建和销毁的问题。单例模式有三个要素:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。
工厂模式:工厂模式主要解决接口选择的问题。该模式下定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,使其创建过程延迟到子类进行。例如选择数据库驱动
观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新
装饰器模式:对已经存在的某些类进行装饰,以此来扩展一些功能,从而动态的为一个对象增加新的功能
原型模式:解决对象的复制问题
备注
C++11 及以上使用 static 局部变量就可以实现 singleton 了,其初始化和线程安全性质由 c++ 语言规范保证
单例模式的优缺点
主要优点:
提供了对唯一实例的受控访问
由于系统中内存只存在一个对象,因此可以节约系统的的资源
主要缺点:
由于单例模式,不是抽象的所以可扩展性比较差
单例类,职责过重,在一定程度上违背了单一职责‘
滥用单例将带来一些负面的问题,如为了节省资源将数据库连接池对象设计为单例模式,可能会导致共享连接池对象的程序过多未出而出现的连接池溢出,如果实例化对象长时间不用系统就会被认为垃圾对象被回收,这将导致对象状态丢失。
单例模式中的饿汉式和懒汉式
懒汉式:在需要的时候才构造对象,除非加锁,否则线程不安全 饿汉式:在类装载的时候就实例化对象,线程安全,无需加锁。此功能由 C++ 保证
常用的 Linux 命令
cat、grep、awk、sed、netstat、ip、firewall-cmd、systemctl、vim、top、ps
段错误的原因
指针越界
栈溢出
迭代器失效
STL 五大组件
容器
算法
迭代器
仿函数
适配器:就是栈
配置器:就是分配内存的
栈的底层实现默认是双端队列,但是只要满足 push_back、pop_back 等操作,使用其它容器也行
C++ 迭代器
输入迭代器:允许读
输出迭代器:允许写
前向迭代器:允许自减操作
双向迭代器:允许双向操作
随机迭代器:允许跳转
new 和 malloc 的区别
new 按照数据类型分配内存,malloc 按照指定的大小分配内存
new 返回的是指定对象的指针,而 malloc 返回的是 void*,因此 malloc 的返回值一般都需要进行类型转化。
new 会调用构造函数,malloc 不会。
new 分配的内存要用 delete 销毁,malloc 要用 free 来销毁;delete 销毁的时候会调用对象的析构函数,而 free 则不会
new 是一个操作符可以重载,malloc 是一个库函数
malloc 分配的内存不够的时候,可以用 realloc 扩容。new 没用这样操作。
new 如果分配失败了会抛出 bad_alloc 的异常,而 malloc 失败了会返回 NULL
申请数组时: new[] 一次分配所有内存,多次调用构造函数,搭配使用 delete[],delete[] 多次调用析构函数,销毁数组中的每个对象。而 malloc 则只能 sizeof(int) * n。
多重继承以及其中存在的问题?多重继承内部是怎么实现的
菱形继承
基类对象的排列顺序和继承时声明的顺序相同
多态
C++ 中的多态分为静态多态(函数重载)和动态多态(虚函数),此外还有模板带来的编译期多态(实际上也是函数重载的一种)