Bilibili - 研发工程师-推荐搜索方向 一面
DPDK是一套开源高性能网络数据平面库,用以加速网络包的处理速度,实现高性能的包交换。通过绕过传统的操作系统网络堆栈,直接在用户空间处理网络包,DPDK能够大幅减少延迟,提高数据包处理的吞吐率,广泛用于需要高速网络通信的场景,如高频交易、云计算数据中心和网络功能虚拟化(NFV)。
C++中的多态主要是通过虚函数实现的。当基类声明一个函数为虚函数时,派生类可以对该函数进行覆盖,以实现不同的功能。这就允许通过基类的指针或引用来调用实际派生类对象的成员函数,具体调用哪个类的函数是在运行时决定的。
实现机制:
virtual关键字。应用场景:
C++中一个空类的大小是1字节。
a的值是未定义的(在C++标准中称为“未初始化”)
unique_ptr可以作为函数返回值。当函数返回一个unique_ptr时,它会利用C++的移动语义将所有权从函数内部转移给调用方。
通常会通过长连接来推送流媒体内容,这是为了确保数据传输的实时性和可靠性。
在32位系统中,最大理论限制约为2GB到4GB;在64位系统中,这个限制远大于物理内存,但实际上受制于系统的物理内存和交换空间。
如果物理机的可用内存加上交换空间(swap space)小于10GB,malloc(10G)将会失败,并返回一个空指针,因为没有足够的空间来分配这么大的内存块。如果可用内存加上交换空间足以满足10GB的请求,malloc将成功分配内存。
Vector 在 C++ STL 中是不线程安全的。不安全的原因主要在于它的操作(如增加、删除元素等)在多线程环境下没有进行同步,可能会导致竞态条件
为了保证在多线程环境下对Vector的操作安全,业务代码需要进行手动的锁控制。
两个线程若同时对Vector的相同索引元素进行修改,将会导致未定义行为,结果可能会是线程中的一个或两个的修改发生,或者导致数据损坏。
定义链表的结构:
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(nullptr) {}
};
解法函数:
ListNode* reverseKGroup(ListNode* head, int k) {
if (head == nullptr || head->next == nullptr || k == 1) return head;
ListNode* dummy = new ListNode(0);
dummy->next = head;
ListNode* prev = dummy;
ListNode* curr = dummy;
ListNode* next = dummy;
int count = 0;
while (curr->next != nullptr) {
curr = curr->next;
count++;
}
while (count >= k) {
curr = prev->next;
next = curr->next;
for (int i = 1; i < k; i++) {
curr->next = next->next;
next->next = prev->next;
prev->next = next;
next = curr->next;
}
prev = curr;
count -= k;
}
return dummy->next;
}
static关键字在编程中有多种作用:
主要作用是:
nullptr或指向任意地址的值,而引用必须被初始化且不能为nullptr。*操作符来访问目标变量的值,引用直接像普通变量一样使用。new和delete进行手动分配和释放的堆内存。静态内存分配与动态内存分配的区别:
new和delete操作符。静态内存分配的优点:
静态内存分配的缺点:
进程是操作系统进行资源分配和调度的基本单位,每个进程拥有独立的地址空间和系统资源。线程是进程中的执行单元,是CPU调度的基本单位,同一进程中的线程共享该进程的地址空间和资源。
线程切换是操作系统的调度器通过保存当前线程的状态到线程的上下文中,然后加载另一个线程的上下文并恢复其状态,这样CPU就可以继续执行新线程的处理。
线程切换时需要保存的上下文包括CPU的寄存器状态、程序计数器、堆栈指针等。这些信息通常保存在系统内存中,具体位置由操作系统决定,通常是在对应线程的内核栈或者线程的控制块中。
IP寄存器,即指令指针寄存器(在x86架构中称为EIP,在x64架构中称为RIP),其作用是存储下一条要执行的指令的地址。它不是通用寄存器,因为它有特定的用途,即指向程序的下一条指令,而不能用于通用数据存储或算术逻辑运算。
LR寄存器是链接寄存器,在ARM架构中常见。它用于存储子程序调用返回后执行的下一条指令的地址。当进行函数或子程序调用时,返回地址会存入LR寄存器。这样,在子程序执行完毕后可以通过LR寄存器找到并返回到调用点继续执行。LR寄存器不是通用寄存器。
创建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)、等待(Waiting)、超时等待(Timed Waiting)和终止(Terminated)。
自旋锁等待时线程处于运行状态,因为它在忙等待,不停地检查锁是否可用。互斥锁等待时,线程处于阻塞状态,不占用CPU资源,直到锁成为可用状态。
进程调度算法是决定哪个进程将接下来使用CPU的规则集合。常见的进程调度算法包括:
unique_ptr:独占所有权,不支持复制和赋值操作。shared_ptr:引用计数机制,多个智能指针可以共享同一个对象。weak_ptr:不对对象的所有权计数,用于解决shared_ptr的循环引用问题。shared_ptr的底层实现通常包括两个主要部分:
Lambda表达式是C++中的匿名函数,让你能够写出内联的、可调用的代码块,可以捕获并使用其所在作用域中的变量。其优点包括:
优点:
应用场景:
map和unordered_map的区别:
map:
unordered_map:
unordered_map通过哈希表实现。它使用一个哈希函数将键映射到桶中,并在桶内使用链表处理哈希冲突。当发生冲突时,即多个元素映射到同一桶,这些元素会以链表形式存储在该桶中。这样可以通过键的哈希值快速访问到对应的桶,从而进行元素的查找、插入和删除。
哈希冲突是指不同的键通过哈希函数计算后得到相同的哈希值,因此它们被映射到同一个哈希表的存储位置上。
TCP三次握手过程:
HTTP协议运行在TCP协议之上,使用TCP提供的可靠传输服务来确保数据正确无误地从客户端传输到服务器,或者反过来。
HTTPS是HTTP协议的安全版本,它通过SSL/TLS协议提供加密处理数据的功能,保证数据传输的安全性和完整性。
static关键字用来定义静态变量,其生命周期为程序执行期间,但它的作用域限定于定义它的文件或函数内。
const关键字定义常量,其值在定义后不能被修改,用来保证数据的不变性。它只影响它所修饰的变量的可变性,而不影响其生命周期。
new 和 delete 是C++中用于动态内存分配和释放的操作符。new 在分配内存的同时调用构造函数初始化对象,delete 释放内存前调用对象的析构函数。malloc 和 free 是C语言中用于动态内存分配和释放的函数。malloc 只分配内存,不初始化,free 只释放内存,不调用析构函数。浅拷贝指的是复制对象的引用,而不是对象本身,因此原始对象和副本对象共享同一块内存地址。深拷贝则是复制对象及其包含的所有对象,副本和原始对象在内存中完全独立。
特点是:
定义一个执行a+b的宏,如下所示:
#define ADD(a, b) ((a) + (b))
这里的 (a) 和 (b) 被括起来是为了避免展开时出现的运算优先级问题。
sizeof 是一个编译时运算符,用来得到某个类型或变量在内存中的大小,单位是字节。
strlen 是一个运行时函数,用来计算字符串的长度,直到遇到第一个空字符 '\0',不包括 '\0'。
sprintf:格式化数据并将结果字符串输出到指定的字符数组。
strcpy:将一个以空字符结尾的字符串复制到另一个地址空间,包括空字符。
memcpy:在内存中复制指定数量的字节,从一个位置到另一个位置,不关注数据类型,可能不处理空字符。
构造函数调用顺序:首先调用父类构造函数,然后调用子类构造函数。
析构函数调用顺序:首先调用子类析构函数,然后调用父类析构函数。
构造函数和析构函数都可以抛出异常,但应谨慎处理,以避免可能导致的资源泄露或不一致状态。
构造函数不能是虚函数,因为虚函数表(vtable)在构造时尚未建立。析构函数可以是虚函数,通常在基类中将析构函数声明为虚析构函数,以确保通过基类指针删除派生类对象时,能正确调用派生类的析构函数。
C++内存对齐是为了提高内存访问效率,确保数据结构按照某个固定的长度(对齐界限)存储。编译器会自动添加填充字节(padding),使得结构体的每个成员相对于结构体开始位置的偏移量是成员大小或某个特定数值(通常是2的幂)的整数倍。
vector扩容通常通过创建一个更大的新内存空间,然后将原有元素复制或移动到新空间,之后释放原始内存空间来实现。扩容的大小通常是当前容量的两倍,以减少频繁扩容的开销。
map 和 set 的特点是它们存储的元素是自动排序的,map 存储键值对,其中键是唯一的,而 set 只存储键且每个键是唯一的。
它们是通过平衡二叉搜索树(通常是红黑树)实现的,这个数据结构可以提供对元素的有序存储,以及在对数时间复杂度内进行元素查找、插入和删除操作。
C++内联函数是一种通常用于优化小型、频繁调用的函数的编程技术。通过在函数声明前加inline关键字,编译器在调用处直接展开函数代码,以减少函数调用的开销。但最终是否内联取决于编译器的决定。
在GDB中进行调试,首先要启动GDB并加载你要调试的程序,可以用 gdb 命令。传递参数可以在GDB中使用 set args 命令来设置命令行参数。然后,你可以使用像 run 来运行程序、break 设置断点、next 单步执行等命令来控制程序的执行并进行调试。
指针的初始化应该将其设为nullptr(C++11及以后)或NULL,以指示它尚未指向任何对象。当指针分配了动态内存后(例如,使用new),完成使用后应使用delete(对于单个对象)或delete[](对于对象数组)来释放分配的内存,然后把指针重新设置为nullptr,避免悬挂指针的问题。
为了协调三个进程对同一块内存区域的读写访问,可以使用互斥锁(mutexes)或读写锁(read-write locks)。互斥锁确保任一时间只有一个进程可以访问该内存。读写锁允许多个读取者同时访问,但写入者独占访问。适当的锁机制需要根据访问模式和性能要求选择。
static关键字用来定义静态变量,其生命周期为程序执行期间,但它的作用域限定于定义它的文件或函数内。
const关键字定义常量,其值在定义后不能被修改,用来保证数据的不变性。它只影响它所修饰的变量的可变性,而不影响其生命周期。
new 和 delete 是C++中用于动态内存分配和释放的操作符。new 在分配内存的同时调用构造函数初始化对象,delete 释放内存前调用对象的析构函数。malloc 和 free 是C语言中用于动态内存分配和释放的函数。malloc 只分配内存,不初始化,free 只释放内存,不调用析构函数。浅拷贝指的是复制对象的引用,而不是对象本身,因此原始对象和副本对象共享同一块内存地址。深拷贝则是复制对象及其包含的所有对象,副本和原始对象在内存中完全独立。
特点是:
定义一个执行a+b的宏,如下所示:
#define ADD(a, b) ((a) + (b))
这里的 (a) 和 (b) 被括起来是为了避免展开时出现的运算优先级问题。
sizeof 是一个编译时运算符,用来得到某个类型或变量在内存中的大小,单位是字节。
strlen 是一个运行时函数,用来计算字符串的长度,直到遇到第一个空字符 '\0',不包括 '\0'。
sprintf:格式化数据并将结果字符串输出到指定的字符数组。
strcpy:将一个以空字符结尾的字符串复制到另一个地址空间,包括空字符。
memcpy:在内存中复制指定数量的字节,从一个位置到另一个位置,不关注数据类型,可能不处理空字符。
构造函数调用顺序:首先调用父类构造函数,然后调用子类构造函数。
析构函数调用顺序:首先调用子类析构函数,然后调用父类析构函数。
构造函数和析构函数都可以抛出异常,但应谨慎处理,以避免可能导致的资源泄露或不一致状态。
构造函数不能是虚函数,因为虚函数表(vtable)在构造时尚未建立。析构函数可以是虚函数,通常在基类中将析构函数声明为虚析构函数,以确保通过基类指针删除派生类对象时,能正确调用派生类的析构函数。
C++内存对齐是为了提高内存访问效率,确保数据结构按照某个固定的长度(对齐界限)存储。编译器会自动添加填充字节(padding),使得结构体的每个成员相对于结构体开始位置的偏移量是成员大小或某个特定数值(通常是2的幂)的整数倍。
vector扩容通常通过创建一个更大的新内存空间,然后将原有元素复制或移动到新空间,之后释放原始内存空间来实现。扩容的大小通常是当前容量的两倍,以减少频繁扩容的开销。
map 和 set 的特点是它们存储的元素是自动排序的,map 存储键值对,其中键是唯一的,而 set 只存储键且每个键是唯一的。
它们是通过平衡二叉搜索树(通常是红黑树)实现的,这个数据结构可以提供对元素的有序存储,以及在对数时间复杂度内进行元素查找、插入和删除操作。
C++内联函数是一种通常用于优化小型、频繁调用的函数的编程技术。通过在函数声明前加inline关键字,编译器在调用处直接展开函数代码,以减少函数调用的开销。但最终是否内联取决于编译器的决定。
在GDB中进行调试,首先要启动GDB并加载你要调试的程序,可以用 gdb 命令。传递参数可以在GDB中使用 set args 命令来设置命令行参数。然后,你可以使用像 run 来运行程序、break 设置断点、next 单步执行等命令来控制程序的执行并进行调试。
指针的初始化应该将其设为nullptr(C++11及以后)或NULL,以指示它尚未指向任何对象。当指针分配了动态内存后(例如,使用new),完成使用后应使用delete(对于单个对象)或delete[](对于对象数组)来释放分配的内存,然后把指针重新设置为nullptr,避免悬挂指针的问题。
为了协调三个进程对同一块内存区域的读写访问,可以使用互斥锁(mutexes)或读写锁(read-write locks)。互斥锁确保任一时间只有一个进程可以访问该内存。读写锁允许多个读取者同时访问,但写入者独占访问。适当的锁机制需要根据访问模式和性能要求选择。
收集整理了一份2024年最新C++开发学习资料,既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C++开发知识点,真正体系化!
包含大厂面经、学习笔记、实战项目、大纲路线、讲解视频 领取地址:
https://docs.qq.com/doc/DR2N4d25LRG1leU9Q
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。