字节小米等后端岗位C++面试题

news2025/1/10 9:16:42

C++ 基础

  • 引用和指针之间的区别?
  • 堆栈和堆中的内存分配有何区别?
  • 存在哪些类型的智能指针?
  • unique_ptr 是如何实现的?我们如何强制在 unique_ptr 中仅存在一个对象所有者?
  • shared_ptr 如何工作?对象之间如何同步引用计数器?
  • 我们可以复制unique_ptr或者将其从一个对象传递到另一个对象吗?
  • 什么是右值和左值?
  • 什么是 std::move 和 std::forward()

面向对象编程(OOP)

  • 访问某些类的私有字段的方法?
  • 一个类可以继承多个类吗?
  • 静态字段是否在类构造函数中初始化?
  • 构造函数/析构函数中会抛出异常吗?如何防止这种情况发生?
  • 什么是虚方法?
  • 为什么我们需要虚拟析构函数?
  • 抽象类和接口的区别?
  • 构造函数可以是虚拟的吗?
  • 关键字 const 如何用于类方法?
  • 如何保护对象不被复制?

STL 容器

  • 向量和列表之间的区别?
  • 地图和无序地图之间的区别?
  • 调用 push_back() 时向量中的迭代器无效吗?
    如何修改你的类以便与 map 和 unordered_map 一起使用?

主题

  • 进程和线程之间的区别?
  • 同一根线程可以重复运行吗?
  • 同步线程的方法?
  • 什么是死锁?

字节跳动中台、后端研发面试题

一面经常提到的问题

1.手撕lru
2.并发编程
3.函数式编程
4.无锁队列
5.CAP
6.线程池,进程池
7.大文件如何对字符串排序
8.线程同步
9.进程状态
10.进程调度、多进程开发,多线程开发
11.线程通信 + 2道场景题选择进程通信方式
12.raws
13.ocket与socket区别
14.设计高性能服务
15.nginx反向代理 epoll,nginx工作模式实现原理
16.pmtu,sendfile,io status等等
17.手撕memcpy 考虑内存重叠与copy效率

二面

1.自我介绍+项目介绍
2.mmap
3.软链接与硬链接
4.free命令 cache与buffer区别
5.数据库 列与行数据库区别
6.线程安全
7.单核线程安全
8.事务
9.隔离性,隔离级别
10.索引,合并
11.http cookie与session
12.跨域请求
13.最大上升子序列个数
14.函数调用过程
15.static c/c++区别
16.项目中的一些问题
17.tcp与udp
18.tcp深挖
19.进程与线程
20.字符串匹配

小米C++相关岗位面试题

1.自我介绍
2.项目介绍
3.实习介绍
4.C++11的新特性
5.懂rapidjson的底层原理?rapidjson使用的过程中遇到过那些bug?
6.判断链表是否有环,求环的入口节点
7.多态实现原理?虚函数指针具体怎么找虚函数表?
8.如何创建进程?
9.进程布局?堆栈全局代码那些区
10.函数压栈过程,包括函数带参数
11.”hello world"存在那个区?
12.线程私有区和共享区
13.是个全局变量但我又想是线程独有的怎么办?
14.tcp三次握手、流量控制、拥塞控制、MSS那一套
15.timewait
16.客户端握手发送SYN,TCP状态机,client变成什么状态?
17.相交链表

C++ 基础

1、引用和指针之间的区别?

引用和指针是C++中两种重要的间接访问机制。引用实质上是一个别名,它在声明时必须初始化,一旦绑定就不能改变。引用不能为空,也不能建立引用的引用,这使得它比指针更安全。引用在内存中实际上是作为一个常量指针实现的,但它对程序员隐藏了指针的复杂性。指针则是一个变量,它存储了另一个变量的内存地址。指针可以为空,可以改变它所指向的对象,也可以进行指针运算。指针的灵活性使它成为一个强大的工具,但同时也容易导致程序错误,如悬空指针和内存泄漏。

2、堆栈和堆中的内存分配有何区别?

栈内存的分配和释放是由编译器自动完成的。当变量超出其作用域时,栈内存会自动释放。栈内存的分配和释放速度很快,因为它使用简单的指针移动来实现。栈内存的大小是固定的,在程序运行时就已确定。
堆内存则是动态分配的,由程序员手动管理或使用智能指针等工具管理。堆内存的生命周期不受作用域限制,可以在需要时分配,在不需要时释放。堆内存的大小理论上只受系统可用内存的限制。但堆内存的分配和释放相对较慢,且容易产生内存碎片。

3、存在哪些类型的智能指针?

智能指针类型: C++11定义了三种智能指针。unique_ptr实现了独占式拥有概念,它保证一个对象只被一个指针拥有。shared_ptr允许多个指针指向同一个对象,通过引用计数机制来管理对象的生命周期。weak_ptr是一种弱引用,它不会增加引用计数,主要用于解决shared_ptr可能产生的循环引用问题。

auto_ptr是C++98引入的智能指针,但由于其危险的拷贝语义已在C++11中被弃用。每种智能指针都有其特定的使用场景,选择合适的智能指针可以大大降低内存管理的复杂性。

4、unique_ptr 是如何实现的?我们如何强制在 unique_ptr 中仅存在一个对象所有者?

unique_ptr的核心思想是独占式拥有。它通过删除拷贝构造函数和拷贝赋值运算符来保证一个对象只能被一个unique_ptr拥有。它支持移动语义,允许在保证安全的情况下转移对象的所有权。
unique_ptr内部包含一个原始指针和一个删除器。删除器是一个可调用对象,负责在unique_ptr析构时释放所管理的资源。unique_ptr的实现非常轻量,在大多数情况下不会带来任何额外的开销。

5、shared_ptr 如何工作?对象之间如何同步引用计数器?

shared_ptr使用引用计数来追踪有多少个shared_ptr共享同一个对象。当引用计数增加时(比如拷贝构造),计数器加一;当引用计数减少时(比如shared_ptr析构),计数器减一。当计数器降为零时,管理的对象会被删除。
为了保证线程安全,引用计数的更新必须是原子操作。shared_ptr内部实际上包含两个指针:一个指向管理的对象,另一个指向控制块。控制块包含引用计数、删除器等信息。这种实现方式使shared_ptr的大小是原始指针的两倍。

6、我们可以复制unique_ptr或者将其从一个对象传递到另一个对象吗?

由于unique_ptr强制独占所有权,所以它不能被复制。但它可以通过std::move来转移所有权。在转移后,原来的unique_ptr会变为空指针,而目标unique_ptr获得对象的所有权。
这种设计使得unique_ptr可以方便地在函数间传递对象的所有权。例如,factory函数可以返回一个unique_ptr,调用者就获得了返回对象的所有权。unique_ptr也可以存储在容器中,但必须使用移动语义来操作。

7、什么是右值和左值?

在C++中,左值是一个位置值,它标识一个持久的对象。左值有一个地址,可以取地址操作符作用于它。典型的左值包括变量名、解引用的指针等。
右值表示临时值,它不能取地址。右值可以分为纯右值(pr-value)和将亡值(x-value)。纯右值是临时的、不具名的值,如字面常量。将亡值是即将被销毁的值,比如即将被移动的对象。

8、什么是 std::move 和 std::forward()

std::move和std::forward: std::move是一个工具函数,它将一个左值强制转换为右值引用,使得我们可以调用移动构造函数或移动赋值运算符。这在需要显式地指明要进行移动操作时非常有用。
std::forward用于完美转发,它保持参数的值类别(左值或右值)不变。在模板编程中,std::forward可以确保参数按照其原始类型被转发,这对于通用库的实现非常重要。

9、访问某些类的私有字段的方法?

访问私有字段的方法: 在C++中,有几种合法的方式可以访问类的私有成员。最常用的是通过友元(friend)声明,可以是友元函数或友元类。友元声明允许外部代码访问类的私有成员。
另一种方式是通过类的公共接口方法来间接访问私有成员。这是面向对象设计中推荐的方式,因为它维护了类的封装性。还可以使用嵌套类,因为嵌套类可以访问外部类的所有成员。

10、一个类可以继承多个类吗?

类的多重继承: C++支持多重继承,即一个类可以同时继承多个基类。但这可能导致菱形继承问题,即一个类通过不同的路径继承了同一个基类的多个实例。
为了解决这个问题,C++引入了虚继承。虚继承确保共同基类只有一个实例。虽然C++支持多重继承,但在实际开发中应该谨慎使用,因为它可能增加代码的复杂性。

11、静态字段是否在类构造函数中初始化?

静态字段的初始化: 静态成员变量属于类而不是对象,它们不在构造函数中初始化。静态成员需要在类外进行定义和初始化,除非是整型或枚举类型的const static成员,这种情况可以在类内初始化。
这样设计的原因是为了避免静态成员被多次初始化。在程序的整个生命周期中,静态成员只需要初始化一次。静态成员的初始化发生在程序开始执行之前。

12、构造函数/析构函数中会抛出异常吗?如何防止这种情况发生?

构造函数/析构函数的异常: 构造函数可以抛出异常,这通常用于指示初始化失败。但析构函数应该避免抛出异常,因为如果在异常处理过程中析构函数抛出异常,程序会立即终止。
为了防止构造函数抛出异常,可以使用try-catch块包装可能抛出异常的代码,或者使用初始化列表来确保资源获取。对于析构函数,应该将其声明为noexcept,并在内部处理所有可能的异常。

13、什么是虚方法?

虚方法: 虚方法是C++实现运行时多态的机制。当基类中声明一个函数为virtual时,派生类可以重写这个函数。在运行时,程序会根据对象的实际类型来调用适当的函数版本。
虚函数通过虚函数表(vtable)来实现。每个包含虚函数的类都有一个vtable,其中存储了该类虚函数的地址。每个对象都包含一个指向vtable的指针(vptr)。这种机制允许在运行时动态绑定函数调用。

14、为什么我们需要虚拟析构函数?

虚析构函数的必要性: 当通过基类指针删除派生类对象时,如果析构函数不是虚函数,则只会调用基类的析构函数,而不会调用派生类的析构函数。这会导致资源泄漏。
因此,当一个类可能作为基类时,其析构函数应该声明为虚函数。这确保了在删除对象时,无论使用什么类型的指针,都能正确调用整个继承链上的析构函数。

15、抽象类和接口的区别?

抽象类和接口的区别: 抽象类是包含至少一个纯虚函数的类。它不能被实例化,只能作为基类使用。抽象类可以包含普通成员函数和数据成员,这些可以在派生类中直接使用。
接口是一种特殊的抽象类,它只包含纯虚函数。在C++中,接口通常被实现为所有函数都是纯虚函数的抽象类。接口定义了一个对象能够做什么,而不规定如何做。

16、构造函数可以是虚拟的吗?

构造函数的虚拟性: 构造函数不能是虚函数。这是因为在调用构造函数时,对象还没有被完全构造,vptr还没有被初始化。因此不能使用虚函数机制。
如果需要根据运行时条件创建不同类型的对象,可以使用工厂模式。工厂模式通过一个静态成员函数来创建对象,这个函数可以根据参数返回不同类型的对象。

17、关键字 const 如何用于类方法?

const关键字在类方法中的使用: const成员函数承诺不会修改对象的状态。它们可以被const对象调用,这提供了一种编译时的类型安全机制。const成员函数不能调用非const成员函数。如果需要在const成员函数中修改某些成员变量,可以将这些变量声明为mutable。mutable允许在const成员函数中修改特定的成员变量,这通常用于缓存等不影响对象逻辑状态的场合。

18、如何保护对象不被复制?

防止对象被复制: 有几种方式可以防止对象被复制。最现代的方式是使用=delete来删除拷贝构造函数和拷贝赋值运算符。这会在编译时阻止任何复制操作。
另一种方式是将拷贝构造函数和拷贝赋值运算符声明为private,并且不提供实现。这也可以防止复制,但错误信息可能不如=delete清晰。

19、向量和列表之间的区别?

vector和list的区别: vector是一种连续存储的容器,它在内存中分配一块连续的空间。这使得vector支持随机访问,但在中间插入或删除元素时需要移动后续元素。当vector需要更多空间时,它会重新分配一个更大的连续空间。
list是一种链式存储的容器,它的元素可以分散在内存的不同位置。list不支持随机访问,但在任何位置插入或删除元素都很快,因为只需要修改相关节点的指针。list的每个元素都需要额外的内存来存储指针。

20、map和unordered_map之间的区别?

map和unordered_map的区别: map基于红黑树实现,它保持键值对按键的顺序存储。这使得map的查找、插入和删除操作的时间复杂度都是O(log n)。map占用的内存较少,而且可以按序遍历。
unordered_map基于哈希表实现,它不保持任何顺序。在理想情况下,查找、插入和删除操作的时间复杂度都是O(1)。但unordered_map需要额外的内存来存储哈希表,而且可能需要处理哈希冲突。

21 调用 push_back() 时向量中的迭代器无效吗?

vector的push_back和迭代器:
当向vector调用push_back时,迭代器的有效性取决于是否发生了内存重新分配。如果vector当前的capacity足够容纳新元素,那么push_back不会导致迭代器失效。但如果capacity不足,vector会分配一个更大的内存块(通常是当前大小的1.5或2倍),并将所有元素复制到新位置,这时之前的所有迭代器都会失效。

为了避免迭代器失效的问题,有以下几种方法:

  1. 预先使用reserve分配足够的空间
  2. 在push_back后重新获取迭代器
  3. 使用索引而不是迭代器来遍历vector
  4. 如果必须使用迭代器,可以先完成迭代器操作,再进行push_back

在实际编程中,建议在知道vector大致容量的情况下,先调用reserve预分配空间。这不仅能避免迭代器失效,还能提高程序性能,因为减少了内存重新分配的次数。

22、如何修改你的类以便与 map 和 unordered_map 一起使用?

修改类使其可用于map和unordered_map:
要让自定义类可以作为map的键,必须为该类实现小于运算符(operator<)。这是因为map内部使用红黑树来组织数据,需要通过比较运算来确定元素的位置。operator<必须满足严格弱序关系,即具有传递性且不能出现等价关系。

对于unordered_map,要求更复杂:

  1. 需要实现相等运算符(operator==)
  2. 需要提供一个特化的std::hash模板,或者自定义哈希函数

实现示例:

class MyClass {
    int id;
    string name;
public:
    // 为map实现小于运算符
    bool operator<(const MyClass& other) const {
        if (id != other.id) return id < other.id;
        return name < other.name;
    }
    
    // 为unordered_map实现相等运算符
    bool operator==(const MyClass& other) const {
        return id == other.id && name == other.name;
    }
};

// 为unordered_map特化hash模板
namespace std {
    template<>
    struct hash<MyClass> {
        size_t operator()(const MyClass& obj) const {
            return hash<int>()(obj.id) ^ hash<string>()(obj.name);
        }
    };
}

哈希函数的质量会直接影响unordered_map的性能。好的哈希函数应该能够均匀分布键值,避免产生过多的哈希冲突。在选择容器时,如果需要保持元素顺序就用map,如果需要最快的查找性能就用unordered_map。

23、同步线程的方法?

在C++中,有多种线程同步机制可以确保多线程程序的正确性:

  1. 互斥锁(mutex):
    最基本的同步机制,用于保护共享资源。C++提供了多种互斥锁:
  • std::mutex:标准互斥锁
  • std::recursive_mutex:允许同一线程多次加锁
  • std::timed_mutex:支持超时的互斥锁
  • std::shared_mutex:读写锁,允许多个读者或一个写者
  1. 条件变量(condition_variable):
    用于线程间的通信和同步,典型用法是等待某个条件成立:
std::mutex mtx;
std::condition_variable cv;
bool ready = false;

// 消费者线程
{
    std::unique_lock<std::mutex> lock(mtx);
    cv.wait(lock, []{ return ready; });
    // 处理数据
}

// 生产者线程
{
    std::lock_guard<std::mutex> lock(mtx);
    ready = true;
    cv.notify_one();
}
  1. 原子操作:
    对于简单的数据类型,使用std::atomic可以避免显式的锁定:
    std::atomic counter{0};
    counter++; // 原子操作,线程安全

  2. 读写锁:
    当读操作远多于写操作时,使用std::shared_mutex可以提高并发性:

std::shared_mutex rwlock;
// 读者
{
    std::shared_lock<std::shared_mutex> lock(rwlock);
    // 读取数据
}
// 写者
{
    std::unique_lock<std::shared_mutex> lock(rwlock);
    // 修改数据
}

选择合适的同步机制取决于具体场景:

  • 简单的共享资源保护用mutex
  • 需要线程间通信用condition_variable
  • 简单数据类型的并发访问用atomic
  • 读多写少的场景用shared_mutex

5.避免死锁一般通过以下方法:

  1. 始终按照相同的顺序获取锁
  2. 使用std::lock同时获取多个锁
  3. 避免在持有锁时调用用户代码
  4. 使用RAII风格的锁管理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2274259.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

超完整Docker学习记录,Docker常用命令详解

前言 关于国内拉取不到docker镜像的问题&#xff0c;可以利用Github Action将需要的镜像转存到阿里云私有仓库&#xff0c;然后再通过阿里云私有仓库去拉取就可以了。 参考项目地址&#xff1a;使用Github Action将国外的Docker镜像转存到阿里云私有仓库 一、Docker简介 Do…

JVM实战—OOM的定位和解决

1.如何对系统的OOM异常进行监控和报警 (1)最佳的解决方案 最佳的OOM监控方案就是&#xff1a;建立一套监控平台&#xff0c;比如搭建Zabbix、Open-Falcon之类的监控平台。如果有监控平台&#xff0c;就可以接入系统异常的监控和报警&#xff0c;可以设置当系统出现OOM异常&…

你知道智能家居与fpc有哪些关联吗?【新立电子】

智能家居&#xff0c;作为现代科技与家居生活深度融合的产物&#xff0c;它不仅仅是一种技术革新&#xff0c;更是一种生活理念的升级&#xff0c;将家居环境打造成为一个更加智能、舒适和安全的生活空间。 智能家居的核心在于其通过互联网、物联网、人工智能等技术手段&#…

STM32 : PWM 基本结构

这张图展示了PWM&#xff08;脉冲宽度调制&#xff09;的基本结构和工作流程。PWM是一种用于控制功率转换器输出电压的技术&#xff0c;通过调整信号的占空比来实现对负载的精确控制。以下是详细讲解&#xff1a; PWM 基本结构 1. 时基单元 ARR (Auto-reload register): 自动…

STM32之一种双通路CAN总线消息备份冗余处理方法(十三)

STM32F407 系列文章 - Dual-CANBus-ProMethod&#xff08;十三&#xff09; 目录 前言 一、现状分析 二、解决思路 1.应用场景网络结构图 2.数据发送流程 3.数据接收流程 4.用到的模块 1.CAN网络速率及时间片分配 2.CAN网络消息ID组成 3.设备节点定义 4.数据格式说明…

内网穿透的应用-Ubuntu本地Docker部署Leantime项目管理工具随时随地在线管理项目

文章目录 前言1.关于Leantime2.本地部署Leantime3.Leantime简单实用4.安装内网穿透5.配置Leantime公网地址6. 配置固定公网地址 前言 本文主要介绍如何在本地Linux系统使用Docker部署Leantime&#xff0c;并结合cpolar内网穿透工具轻松实现随时随地查看浏览器页面&#xff0c;…

VulnHub-Acid(1/100)

参考链接&#xff1a; ​​​​​​​【VulnHub】Acid靶场复盘-CSDN博客 靶场渗透&#xff08;二&#xff09;——Acid渗透_ambassador 靶场渗透-CSDN博客 网络安全从0到0.5之Acid靶机实战渗透测试 | CN-SEC 中文网 Vulnhub靶场渗透练习(四) Acid - 紅人 - 博客园 红日团队…

HTML5实现好看的端午节网页源码

HTML5实现好看的端午节网页源码 前言一、设计来源1.1 网站首页界面1.2 登录注册界面1.3 端午节由来界面1.4 端午节习俗界面1.5 端午节文化界面1.6 端午节美食界面1.7 端午节故事界面1.8 端午节民谣界面1.9 联系我们界面 二、效果和源码2.1 动态效果2.2 源代码 源码下载结束语 H…

git merge与rebase区别以及实际应用

在 Git 中&#xff0c;merge 和 rebase 是两种将分支的更改合并到一起的常用方法。虽然它们都可以实现类似的目标&#xff0c;但它们的工作方式和效果有所不同。 1. Git Merge 定义&#xff1a;git merge 是将两个分支的历史合并在一起的一种操作。当你执行 git merge 时&…

Matlab APP Designer

我想给聚类的代码加一个图形化界面&#xff0c;需要输入一些数据和一些参数并输出聚类后的图像和一些评价指标的值。 gpt说 可以用 app designer 界面元素设计 在 设计视图 中直接拖动即可 如图1&#xff0c;我拖进去一个 按钮 &#xff0c;图2 红色部分 出现一行 Button 图…

PyCharm 引用其他路径下的文件报错 ModuleNotFound 或报红

PyCharm 中引用其他路径下的文件提示 ModuleNotFound&#xff0c;将被引用目录添加到系统路径&#xff1a; # # 获取当前目录 dir_path os.path.dirname(os.path.realpath(__file__)) # # 获取上级目录 parent_dir_path os.path.abspath(os.path.join(dir_path, os.pardir))…

【HarmonyOS NEXT】鸿蒙应用点9图的处理(draw9patch)

【HarmonyOS NEXT】鸿蒙应用点9图的处理&#xff08;draw9patch&#xff09; 一、前言&#xff1a; 首先在鸿蒙中是不支持安卓 .9图的图片直接使用。只有类似拉伸的处理方案&#xff0c;鸿蒙提供的Image组件有与点九图相同功能的API设置。 可以通过设置resizable属性来设置R…

SOLID原则学习,开闭原则

文章目录 1. 定义2. 开闭原则的详细解释3. 实现开闭原则的方法4. 总结 1. 定义 开闭原则&#xff08;Open-Closed Principle&#xff0c;OCP&#xff09;是面向对象设计中的五大原则&#xff08;SOLID&#xff09;之一&#xff0c;由Bertrand Meyer提出。开闭原则的核心思想是…

【Vue3中使用crypto-js】crypto-js加密解密用法

目录 1、安装crypto2、创建crypto.js文件3、在main.js主文件中进行引用4、页面中进行使用5、实现效果展示6、加密模式解析以及iv参数使用 1、安装crypto npm install crypto-js 如果是在Typescript版本需要再安装 npm install --save types/crypto-js2、创建crypto.js文件 注…

跨界融合:人工智能与区块链如何重新定义数据安全?

引言&#xff1a;数据安全的挑战与现状 在信息化驱动的数字化时代&#xff0c;数据已成为企业和个人最重要的资产之一。然而&#xff0c;随着网络技术的逐步优化和数据量的爆发式增长&#xff0c;数据安全问题也愈变突出。 数据安全现状&#xff1a;– 数据泄露驱动相关事件驱…

简单易用的PDF工具箱

软件介绍 PDF24 Creator是一款简单易用的PDF工具箱&#xff0c;而且完全免费&#xff0c;没有任何功能限制。既可以访问官网在线使用各种PDF工具&#xff0c;也可以下载软件离线使用各种PDF工具。 软件功能 1、PDF转换 支持将多种文件格式&#xff08;Word、PowerPoint、Exc…

低秩信息收集_0109

系列博客目录 文章目录 系列博客目录LoRA: Low-Rank Adaptation of Large Language Models传统模型适配的局限性&#xff1a;尽管研究界致力于通过添加适配器层或优化输入层激活来提高模型适配效率&#xff0c;这些方法在大型模型和延迟敏感的环境中存在局限。适配器层尽管参数…

C语言与ASCII码应用之简单加密

加密是什么&#xff1f;什么是加密通话&#xff1f;用人话说就是一句有含义的话&#xff0c;经过一定的特殊规则把里面的每个字按照这个规则进行改变&#xff0c;但是这个规则只有你和你想让知道这条信息的人知道 今天我们来用ASCII码编写一个简单加密与解密的程序&#xff0c…

国产3D CAD将逐步取代国外软件

在工业软件的关键领域&#xff0c;计算机辅助设计&#xff08;CAD&#xff09;软件对于制造业的重要性不言而喻。近年来&#xff0c;国产 CAD 的发展态势迅猛&#xff0c;展现出巨大的潜力与机遇&#xff0c;正逐步改变着 CAD 市场长期由国外软件主导的格局。 国产CAD发展现状 …

【Linux网络编程】第二十二弹---深入理解 I/O 多路转接之 epoll:系统调用、工作原理、代码演示及应用场景

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】【Linux网络编程】 目录 1、I/O 多路转接之 epoll 1.1、epoll 初识 1.2、epoll 的相关系统调用 1.2.1、epoll_create 1.2.2、epol…