面试提问(1)
面试提问
- 1.你能说一说C/C++之间的区别吗?
- 2.你能将一些你对构造函数和析构函数的认识吗?
- 3.讲一下继承和多态
- 4.你了解TCP/IP四层网络模型吗?
- 5.你了解三次握手和四次挥手吗?
- 6.讲一下进程和线程?
- 7.你对二叉树的了解有哪些?
- 8.hash表中是怎么解决hash冲突的?
- 9.vector和list的区别有哪些?
- 10.进程间通信的方法有哪些?
- 11.你对管道的理解?
- Linux管道的工作原理与实现机制
- 一、基本概念
- 二、工作原理
- 三、实现机制
- 四、应用场景
- 五、优缺点分析
1.你能说一说C/C++之间的区别吗?
- 从编程范式来讲:C语言是面向过程的语言,他更注重的是函数和过程;而C++语言是面向对象语言,支持类的封装,继承和多态,并且C++还是一个泛型编程语言。
- 特性扩展:C++在C的基础上引入了类,模版,异常处理,预算福重载,引用等。其中模版是支持泛型编程的重要特性,并且在模版的基础上有了STL标准库。
- 在C++11后,又引入了很多的重要特性,比如右值引用,智能指针,模版的可变参数等重要特性。
- 右值引用:的出现,解决了部分返回值拷贝所带来的空间开销
- 智能指针:使用了RAII思想,更好的对资源进行管理,防止出现内存泄漏的重要手段之一。
- 模版的可变参数:使模版更加的灵活多变。
2.你能将一些你对构造函数和析构函数的认识吗?
- 在一个类中有默认成员函数:构造函数,析构函数,拷贝构造,复制拷贝,移动拷贝构造,移动复制拷贝
- 构造函数在创建对象的时候自动调用,析构函数实在对象生命周期结束时自动调用
- 构造函数可以进行重载,析构函数不支持重载。
- 构造函数,析构函数在多态下的注意事项:
- 在处于多态的状况下,构造函数不能为虚函数,而析构函数要求是虚函数。
- 其中构造函数之所以不能是虚函数主要是:构成多态的要求,1.有继承关系,2.有虚函数,3.基类的指针或者引用指向派生类。而一旦类中有虚函数,那么类中就会与一个虚函数表指针,这个指针指向的是一个虚函数表,而虚函数表是在编译阶段由编译器自动生成的,并存放在代码段中。而虚函数表指针则是在创建对象时(运行时)的初始化类表中进行初始化的,所以构造函数不能定义为虚函数。
- 而析构函数要求时虚函数,因为如果析构函数不是虚函数,派生类在进行对象销毁时,由于析构函数不是虚函数,所以只会进行调用基类的析构函数,而派生类的析构函数则不会进行调用,一旦派生类中涉及到资源管理,就会导致内存泄漏。
3.讲一下继承和多态
- 继承:派生类继承基类的范式有三种,public(公有),protected(保护),private(私有)。
- 多态:通虚函数(virtual)实现动态绑定
- 静态多态:在编译阶段就确定了函数的地址——函数重载,模版
- 动态多态:在运行时才能确定函数的地址——虚函数
4.你了解TCP/IP四层网络模型吗?
TCP/IP四层网络模型
-
应用层(HTTP、FTP):提供用户接口,处理数据格式。
-
传输层(TCP、UDP):保证端到端通信,TCP可靠,UDP高效。
-
网络层(IP、ICMP):路由寻址,选择最佳路径传输数据包。
-
网络接口层(以太网、Wi-Fi):物理介质传输原始比特流。
-
对比OSI七层:TCP/IP将OSI的会话层、表示层合并到应用层,数据链路层与物理层合并为网络接口层。
5.你了解三次握手和四次挥手吗?
-
三次握手(建立连接):
-
客户端发送SYN(seq=x)到服务器,进入SYN_SENT状态。
-
服务器回复SYN-ACK(seq=y, ack=x+1),进入SYN_RCVD状态。
-
客户端发送ACK(ack=y+1),双方进入ESTABLISHED状态。
-
-
四次挥手(断开连接):
-
主动方发送FIN(seq=u),进入FIN_WAIT_1状态。
-
被动方回复ACK(ack=u+1),进入CLOSE_WAIT状态,主动方进入FIN_WAIT_2。
-
被动方发送FIN(seq=v),进入LAST_ACK状态。
-
主动方回复ACK(ack=v+1),进入TIME_WAIT(等待2MSL),确保被动方收到ACK。
-
6.讲一下进程和线程?
- 定义方面
- 进程是承担系统分配资源的实体,同一进程内线程共享其资源
- 线程是CPU调度的基本单位
- 一个进程中可以有多个线程,但是至少要有一个线程
- 创建方面
- 线程创建的代价更小,只需要创建对应的TCB,共享同一地址空间,每个线程都有自己的独立栈空间
- 进程创建则需要创建PCB,地址空间,页表等
- 调度方面
- CPU调度一个进程其实真正调度的是进程中的线程线程,在linux其实叫做轻量级进程
- 调度进程的代价比调度线程的代价要高
- 进程/线程切换方面
- 线程切换只需要切换TCB和自身上下文数据,由于线程是共享进程的资源的,所以在切换到时候并不需要每次都加载地址空间的数据
- 而进程切换则需要切换地址空间,页表,映射关系,特别是上下切换catch寄存器中的数据消耗大。
7.你对二叉树的了解有哪些?
- 常见的二叉树:
- 结构:每个节点最多有两个子节点(左、右子树)。
- 二叉树的一些特例:
- 搜索二叉树:每个节点中的左子树上的数都会比他小,每个节点的右子树的数都会比他大。在对无序数据(没有重复数据)的数据进行构造搜索二叉树,他的搜索效果可以达到O(log(n))。但是有个特例就是如果是有序的数据进行构建搜索二叉树的话就会出现一边倒的情况,这个时候搜索事件就变成了O(n).
- 所以为了出现搜索二叉树出现一边倒的情况就有了AVL树:在搜索二叉树的基础上添加了旋转因子,每个节点的左子树和右子树高度差不能超过1,一旦超过了就进行旋转。但是AVL树在进行大量的数据删除的情况下的效率不是很好,因为会存在大量的旋转操作。
- 所以在AVL树的基础上又出现了红黑树:红黑树有以下规则:节点要么是红色的要么是黑色的,根节点和叶子节点都是黑色的,红色节点的两个孩子节点一定是黑色的,任意一个节点上的所有路径上的黑色节点数是相同的,最长节点不能超过最短节点长度的两倍所以在进行删除数据的时候红黑树树的旋转次数是不会很多的,并且查询速度也不会慢AVL树多少。
8.hash表中是怎么解决hash冲突的?
- hash表是用的数组进行实现的,是通过哈希函数确定数据放到那个位置,而通过哈希函数计算出的key值是会出现相同的数据的,这个就叫做哈希冲突,而解决哈希冲突的方法有:
- 设计更好的哈希函数
- 除留余数法(常用)
- 直接定值法(常用)
- 通过从产生冲突的地方进行查询后续的位置进行存放——线性探测,二次探测
- 开散列——每个数组中存放的是一个队列,发生冲突的地方进行链表的尾插。
- 设计更好的哈希函数
9.vector和list的区别有哪些?
- 存储方式:vector的内存是连续的,而list不是连续的
- 优缺点:vector支持随机访问,list不支持随机访问,vector和list在尾部增删数据都很快,但是vector在中间位置进行增删数据时需要移动柜后面的的数据,效率较低,而list在任意位置增删数据都很快。
- 扩容方面:vector可以自动进行扩容,而list则是会进行开一块空间进行连接,所以list会有内存碎片现象。
- 迭代器失效:vector在进行添加数据时,如果进行了扩容的话会有迭代器失效 的问题,而list不会。
10.进程间通信的方法有哪些?
-
管道(Pipe):单向通信,常用于父子进程。
-
消息队列:通过消息缓冲区传递结构化数据。
-
共享内存:多个进程访问同一内存区域,需同步机制(如信号量)。
-
套接字(Socket):跨网络通信,支持TCP/UDP。
信号(Signal):异步通知进程事件(如SIGKILL终止进程)
11.你对管道的理解?
Linux管道的工作原理与实现机制
一、基本概念
Linux管道(Pipe)是一种进程间通信(IPC)机制,允许数据以单向流的形式从一个进程传递到另一个进程。它通过内核缓冲区实现数据传输,常用于命令行中连接多个命令的输出和输入。例如:
$ command1 | command2
此时command1
的输出会作为command2
的输入。
二、工作原理
-
匿名管道(无名管道)
- 单向通信:数据仅能单向流动(半双工)
- 生命周期:随进程结束而销毁
- 实现方式:通过
pipe()
系统调用创建,返回两个文件描述符fd[0]
(读端)和fd[1]
(写端) - 典型场景:父子进程间通信(通过
fork()
共享文件描述符)
-
命名管道(FIFO)
- 持久化管道:通过
mkfifo
创建文件系统可见的管道文件 - 独立进程通信:允许无亲缘关系的进程通过文件名访问
- 全双工支持:需手动实现双向通信
- 持久化管道:通过
三、实现机制
-
内核数据结构
管道在内核中表现为一个环形缓冲区(通常默认大小为 4 KB 4\,\text{KB} 4KB,可通过fcntl()
调整),由struct pipe_inode_info
管理读写指针和缓冲区状态。 -
读写规则
- 写操作:当缓冲区满时,写进程阻塞
- 读操作:当缓冲区空时,读进程阻塞
- 关闭规则:
close(fd[1]); // 关闭写端后,读端返回EOF close(fd[0]); // 关闭读端后,写进程会收到SIGPIPE信号
-
系统调用流程
int fd[2]; pipe(fd); // 创建管道 if (fork() == 0) { close(fd[0]); // 子进程关闭读端 write(fd[1], buf, len); } else { close(fd[1]); // 父进程关闭写端 read(fd[0], buf, len); }
四、应用场景
-
命令行流水线
$ cat access.log | grep "404" | cut -d' ' -f1 | sort | uniq -c
通过多个管道实现日志分析流水线。
-
进程间大数据传输
避免临时文件产生的I/O开销,适合流式数据处理。 -
控制流同步
通过阻塞机制协调生产者和消费者的执行节奏。
五、优缺点分析
优势 | 限制 |
---|---|
低内存拷贝(内核缓冲区直接传递) | 单向通信 |
天然同步机制 | 缓冲区大小限制 |
无需持久化存储 | 仅适用于线性数据处理 |