春招-Bilibili - 后端
Bilibili - 研发工程师-推荐搜索方向 一面
DPDK了解吗
DPDK是一套开源高性能网络数据平面库,用以加速网络包的处理速度,实现高性能的包交换。通过绕过传统的操作系统网络堆栈,直接在用户空间处理网络包,DPDK能够大幅减少延迟,提高数据包处理的吞吐率,广泛用于需要高速网络通信的场景,如高频交易、云计算数据中心和网络功能虚拟化(NFV)。
C++多态的实现,以及应用场景
C++中的多态主要是通过虚函数实现的。当基类声明一个函数为虚函数时,派生类可以对该函数进行覆盖,以实现不同的功能。这就允许通过基类的指针或引用来调用实际派生类对象的成员函数,具体调用哪个类的函数是在运行时决定的。
实现机制:
- 基类中声明虚函数,使用
virtual
关键字。 - 派生类中重写该虚函数。
- 通过基类指针或引用调用虚函数时,动态绑定至派生类的实现。
应用场景:
- 当有多个派生类时,可以用同一个基类指针或引用来操作不同的派生类对象,实现代码的统一管理。
- 在设计模式中,例如工厂方法模式、策略模式、状态模式等,多态性允许用户使用接口类型的通用代码,同时传入不同的派生类实例以改变行为。
- 当开发库或框架时,多态允许用户通过继承和重写方法来扩展和自定义功能,而不需要修改原有的库代码。
一个空类的大小
C++中一个空类的大小是1字节。
class 里面定义int a,如果不实现构造函数,实例化这个类,a的值是?
a
的值是未定义的(在C++标准中称为“未初始化”)
unique_ptr可以作为函数返回值吗
unique_ptr
可以作为函数返回值。当函数返回一个unique_ptr
时,它会利用C++的移动语义将所有权从函数内部转移给调用方。
快手直播流媒体是走长连接网关推送的吗
通常会通过长连接来推送流媒体内容,这是为了确保数据传输的实时性和可靠性。
HTTP3.0 对比HTTP 2.0 的优势
- 基于QUIC协议:HTTP/3使用QUIC(快速UDP互联网连接)代替TCP,使得连接建立更快,减少了握手时间。
- 提高了传输效率:QUIC支持多路复用,但与HTTP/2不同,它避免了TCP的“队头阻塞”问题,使得即使部分数据丢失也不会影响其他数据的传输。
- 更好的错误恢复:QUIC在包级别实现了前向纠错和快速重传机制,减少了因为丢包导致的延迟。
- 内置TLS加密:QUIC默认内置了TLS 1.3,提高了数据传输的安全性。
- 更好的连接迁移:支持连接ID,即使用户的IP地址变化,也能无缝继续通信。
HTTP 2.0 对比 HTTP 1.1的优势
- 多路复用: HTTP/2通过在一个TCP连接上同时发送多个请求和接收多个响应消息,来消除不必要的延迟并提高页面加载速度。
- 头部压缩: HTTP/2引入了头部压缩机制,减小了数据包的大小并提高了传输效率。
- 服务器推送: HTTP/2可以让服务器把客户端需要的资源主动“推送”给客户端,减少了往返的数据交换次数。
- 流优先级: 在HTTP/2中,客户端可以设置资源的优先级,使得重要的资源可以优先加载,从而提高了用户体验。
- 二进制协议: HTTP/2是二进制协议,不再是文本协议,二进制协议解析更高效,更少错误。
讲一下进程 线程 协程的区别
- 进程:是操作系统分配资源的最小单位,具备一定独立功能,能运行在处理器上,每个进程有自己的独立内存空间。
- 线程:是程序执行的最小单位,线程是进程内部的一个实体,是比进程更小的能独立运行的基本单位,一个进程中可以有多个线程,这些线程共享进程的资源。
- 协程:是一种用户态的轻量级线程,也就是协程的切换不需要操作系统参与,由系统用户自行调度,因此效率更高。协程可以理解为"协作的线程"。它比线程更小,因为它只使用了很少的内存大小。
一个进程调用malloc最大能分配多大内存
在32位系统中,最大理论限制约为2GB到4GB;在64位系统中,这个限制远大于物理内存,但实际上受制于系统的物理内存和交换空间。
物理机malloc(10G)会发生什么
如果物理机的可用内存加上交换空间(swap space)小于10GB,malloc(10G)将会失败,并返回一个空指针,因为没有足够的空间来分配这么大的内存块。如果可用内存加上交换空间足以满足10GB的请求,malloc将成功分配内存。
Vector线程安全吗,不安全在哪
Vector 在 C++ STL 中是不线程安全的。不安全的原因主要在于它的操作(如增加、删除元素等)在多线程环境下没有进行同步,可能会导致竞态条件
多线程下使用Vector一定要加锁吗
为了保证在多线程环境下对Vector的操作安全,业务代码需要进行手动的锁控制。
两个线程同时对Vector下相同索引的元素修改会发生什么
两个线程若同时对Vector的相同索引元素进行修改,将会导致未定义行为,结果可能会是线程中的一个或两个的修改发生,或者导致数据损坏。
C++ 内存序介绍一下
- memory_order_relaxed:放宽内存顺序,不要求操作之间的顺序。
- memory_order_consume:较为弱的顺序要求,仅在特定平台上有效。
- memory_order_acquire:阻止操作重排序到原子操作之前。
- memory_order_release:阻止操作重排序到原子操作之后。
- memory_order_acq_rel:同时应用 acquire 和 release。
- memory_order_seq_cst:顺序一致,所有线程看到的操作顺序相同。
手撕:k个一组反转链表
定义链表的结构:
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;
}
小米C++面试
static 关键字的作用
static
关键字在编程中有多种作用:
- 在类的成员变量前使用,表示该变量属于类本身,而不是任何类的实例。
- 在类的成员函数前使用,表示该函数不需要对象实例即可调用,且只能访问类的静态成员变量和其他静态成员函数。
- 在局部变量前使用,表示该变量在函数调用结束后不会被销毁,而是保持其值不变。
- 在全局变量或函数前使用,限制其作用范围仅在定义的文件内,对其他文件不可见。
extern 关键字的作用
主要作用是:
- 允许在多个文件中访问同一个全局变量或函数。
- 表明变量或函数的定义存在于其他文件中。
指针和引用的区别
- 指针是一个存储变量地址的变量,而引用是一个变量的别名。
- 指针可以被重新赋值以指向另一个不同的地址,但引用一旦绑定到一个对象,就不能被改变指向另一个对象。
- 指针可以是
nullptr
或指向任意地址的值,而引用必须被初始化且不能为nullptr
。 - 指针使用
*
操作符来访问目标变量的值,引用直接像普通变量一样使用。
c++内存分配的方式
- 静态存储:编译时分配,如全局变量、静态变量。
- 自动存储:函数内部声明的变量,如局部变量,随着函数调用创建和退出销毁。
- 动态存储:使用
new
和delete
进行手动分配和释放的堆内存。
静态内存分配和动态内存分配的区别,静态分配的优缺点
静态内存分配与动态内存分配的区别:
- 静态内存分配在编译时就确定了存储空间的大小和生命周期,通常用于全局变量和静态局部变量。
- 动态内存分配在运行时根据需要动态地分配和释放内存,通常使用
new
和delete
操作符。
静态内存分配的优点:
- 管理简单,没有额外的运行时开销。
- 生命周期长,随程序启动创建,程序结束时销毁。
静态内存分配的缺点:
- 灵活性低,必须提前预知并定义所需内存大小。
- 可能会导致内存浪费,若预分配内存未被充分利用。
互斥锁和自旋锁的区别
- 互斥锁:当一个线程获得互斥锁后,其他尝试获得该锁的线程会被挂起(阻塞),直到锁被释放。适用于线程执行时间较长的情况。
- 自旋锁:当一个线程尝试获取自旋锁而锁已被占用时,线程会循环等待(自旋),直到锁被释放。适用于线程执行时间非常短的情况,避免了线程挂起的开销。
线程和进程的区别
进程是操作系统进行资源分配和调度的基本单位,每个进程拥有独立的地址空间和系统资源。线程是进程中的执行单元,是CPU调度的基本单位,同一进程中的线程共享该进程的地址空间和资源。
如何进行线程切换的?
线程切换是操作系统的调度器通过保存当前线程的状态到线程的上下文中,然后加载另一个线程的上下文并恢复其状态,这样CPU就可以继续执行新线程的处理。
线程切换需要保存的上下文、保存在哪里?
线程切换时需要保存的上下文包括CPU的寄存器状态、程序计数器、堆栈指针等。这些信息通常保存在系统内存中,具体位置由操作系统决定,通常是在对应线程的内核栈或者线程的控制块中。
IP寄存器的作用,是通用寄存器吗?
IP寄存器,即指令指针寄存器(在x86架构中称为EIP,在x64架构中称为RIP),其作用是存储下一条要执行的指令的地址。它不是通用寄存器,因为它有特定的用途,即指向程序的下一条指令,而不能用于通用数据存储或算术逻辑运算。
LR寄存器了解吗
LR寄存器是链接寄存器,在ARM架构中常见。它用于存储子程序调用返回后执行的下一条指令的地址。当进行函数或子程序调用时,返回地址会存入LR寄存器。这样,在子程序执行完毕后可以通过LR寄存器找到并返回到调用点继续执行。LR寄存器不是通用寄存器。
线程有哪几种状态?
创建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)、等待(Waiting)、超时等待(Timed Waiting)和终止(Terminated)。
自旋锁等待时线程处于什么状态?互斥锁呢?
自旋锁等待时线程处于运行状态,因为它在忙等待,不停地检查锁是否可用。互斥锁等待时,线程处于阻塞状态,不占用CPU资源,直到锁成为可用状态。
讲一下你了解的进程调度算法
进程调度算法是决定哪个进程将接下来使用CPU的规则集合。常见的进程调度算法包括:
- 先来先服务(FCFS):按照进程到达的顺序进行调度。
- 最短作业优先(SJF):先执行预计运行时间最短的进程。
- 优先级调度:优先执行优先级最高的进程。
- 轮转调度(Round Robin,RR):每个进程分配时间片,轮流执行。
- 多级反馈队列(Multilevel Feedback Queue,MFQ):动态调整进程的优先级和时间片。
讲一下c++智能指针
-
unique_ptr
:独占所有权,不支持复制和赋值操作。 -
shared_ptr
:引用计数机制,多个智能指针可以共享同一个对象。 -
weak_ptr
:不对对象的所有权计数,用于解决shared_ptr
的循环引用问题。
shared_ptr的底层实现了解吗?
shared_ptr
的底层实现通常包括两个主要部分:
- 控制块:存储引用计数和指向动态分配的对象的指针。
- 智能指针对象:包含对控制块的引用。
讲一下lambda表达式,lambda表达式优点和应用场景
Lambda表达式是C++中的匿名函数,让你能够写出内联的、可调用的代码块,可以捕获并使用其所在作用域中的变量。其优点包括:
优点:
- 简洁:减少了编写单独函数或函数对象的必要。
- 方便:可直接在需要的地方定义和使用。
- 灵活:能够捕获所在作用域的变量。
应用场景:
- 作为回调函数,例如给线程或算法传参。
- 在STL算法中,用于自定义排序准则或操作。
- 用于定义局部的小功能块,避免编写多余的函数。
map 和 unordered_map 区别
map
和unordered_map
的区别:
map
:
- 基于红黑树实现。
- 元素按键自动排序。
- 查找、插入和删除操作的时间复杂度为O(log n)。
unordered_map
:
- 基于哈希表实现。
- 元素不排序,按哈希值存储。
- 平均情况下查找、插入和删除操作的时间复杂度为O(1),最坏情况为O(n)。
unordered_map 实现了解吗?
unordered_map
通过哈希表实现。它使用一个哈希函数将键映射到桶中,并在桶内使用链表处理哈希冲突。当发生冲突时,即多个元素映射到同一桶,这些元素会以链表形式存储在该桶中。这样可以通过键的哈希值快速访问到对应的桶,从而进行元素的查找、插入和删除。
哈希冲突是指什么?
哈希冲突是指不同的键通过哈希函数计算后得到相同的哈希值,因此它们被映射到同一个哈希表的存储位置上。
讲一下TCP三次握手
TCP三次握手过程:
- 客户端发送一个含有SYN标志的数据包给服务器,请求连接。
- 服务器收到SYN包,回应一个含有SYN/ACK标志的数据包,表示确认。
- 客户端收到SYN/ACK包,再发送一个ACK包给服务器,完成连接建立。
http协议和TCP协议的关系
HTTP协议运行在TCP协议之上,使用TCP提供的可靠传输服务来确保数据正确无误地从客户端传输到服务器,或者反过来。
https协议和http协议的关系
HTTPS是HTTP协议的安全版本,它通过SSL/TLS协议提供加密处理数据的功能,保证数据传输的安全性和完整性。
浩鲸云计算C++
内存分配情况,存放在哪里。
- 堆:动态分配内存,用于存放动态创建的对象。
- 栈:自动分配和释放,存放函数局部变量和函数参数。
- 数据区:存放全局变量和静态变量。
- 代码区:存放程序的执行代码。
函数参数传递的方式和特点。
- 值传递:函数接收参数值的一个副本,原始数据不会被函数修改。
- 引用传递:函数接收参数的内存地址(引用),可以直接修改原始数据。
static和const的区别。
static
关键字用来定义静态变量,其生命周期为程序执行期间,但它的作用域限定于定义它的文件或函数内。
const
关键字定义常量,其值在定义后不能被修改,用来保证数据的不变性。它只影响它所修饰的变量的可变性,而不影响其生命周期。
static修饰局部变量、全局变量、函数和类各有什么特点。
- 静态局部变量:存储在数据区,生命周期贯穿程序执行期,但只在定义它的函数内可见。
- 静态全局变量:作用域限定于定义它的文件内,对其他文件不可见。
- 静态函数:其链接属性为内部,只能在定义它的文件内使用。
- 静态类成员:属于类本身,而不是类的任何特定对象,所有对象共享同一个静态成员。
new和malloc,free和delete区别。
-
new
和delete
是C++中用于动态内存分配和释放的操作符。new
在分配内存的同时调用构造函数初始化对象,delete
释放内存前调用对象的析构函数。 -
malloc
和free
是C语言中用于动态内存分配和释放的函数。malloc
只分配内存,不初始化,free
只释放内存,不调用析构函数。
指针和引用区别。
- 指针是一个变量,其值为另一个变量的地址,通过地址,可以直接访问和修改对应内存中的值。
- 引用是别名,它为对象提供了一个新的名字,对引用的操作等同于对对象本身的操作。
- 指针可以为空,引用必须绑定到一个对象。
- 指针的值(即所指对象的地址)可以改变,但引用一旦与某个对象绑定,就不能再改变引用到其他对象。
深拷贝和浅拷贝。
浅拷贝指的是复制对象的引用,而不是对象本身,因此原始对象和副本对象共享同一块内存地址。深拷贝则是复制对象及其包含的所有对象,副本和原始对象在内存中完全独立。
进程间通信,管道的特点。
- 半双工通信:数据只能单向流动,如果需要双向通信,需要创建两个管道。
- 数据流动是顺序的和阻塞的:数据按照发送的顺序接收,如果管道空则读操作阻塞,如果管道满则写操作阻塞。
- 管道是用于有亲缘关系的进程间通信,如父子进程通信。
define特点,怎么定义一个a+b的宏。
特点是:
- 预处理阶段进行文本替换,不涉及类型检查。
- 可定义常量和宏,提高代码复用性。
- 宏可以包含参数,但不进行正确性检查。
定义一个执行a+b的宏,如下所示:
#define ADD(a, b) ((a) + (b))
这里的 (a)
和 (b)
被括起来是为了避免展开时出现的运算优先级问题。
sizeof和strlen区别。
sizeof
是一个编译时运算符,用来得到某个类型或变量在内存中的大小,单位是字节。
strlen
是一个运行时函数,用来计算字符串的长度,直到遇到第一个空字符 '
,不包括 '
'sprintf、strcpy和memcpy区别。
'sprintf
。
strcpy
memcpy
:格式化数据并将结果字符串输出到指定的字符数组。
虚函数特点。
:将一个以空字符结尾的字符串复制到另一个地址空间,包括空字符。- 存在虚表(vtable):每个有虚函数的类都有一个虚表。
- 可以被派生类重写:派生类可以提供自己的虚函数实现。
- 必须至少有一个函数体,除非声明为纯虚函数。
父类子类构造析构函数调用顺序。
构造析构能否抛出异常,能否是虚函数。
内存对齐。
构造函数调用顺序:首先调用父类构造函数,然后调用子类构造函数。
析构函数调用顺序:首先调用子类析构函数,然后调用父类析构函数。
STL有哪些容器,各自特点。
构造函数和析构函数都可以抛出异常,但应谨慎处理,以避免可能导致的资源泄露或不一致状态。
构造函数不能是虚函数,因为虚函数表(vtable)在构造时尚未建立。析构函数可以是虚函数,通常在基类中将析构函数声明为虚析构函数,以确保通过基类指针删除派生类对象时,能正确调用派生类的析构函数。
序列容器C++内存对齐是为了提高内存访问效率,确保数据结构按照某个固定的长度(对齐界限)存储。编译器会自动添加填充字节(padding),使得结构体的每个成员相对于结构体开始位置的偏移量是成员大小或某个特定数值(通常是2的幂)的整数倍。
vector- list:
- deque:动态数组,支持快速随机访问。
- 关联容器:双向链表,支持快速插入和删除。
- set:双端队列,两端都可以快速插入和删除。
- map:
- multiset:集合,元素唯一且自动排序。
- multimap:键值对集合,键唯一且自动排序。
- 无序关联容器(C++11新增):集合,元素可以重复,自动排序。
- unordered_set:键值对集合,键可以重复,自动排序。
- unordered_map:
- unordered_multiset:集合,元素唯一,基于哈希表实现,不排序。
- unordered_multimap:键值对集合,键唯一,基于哈希表实现,不排序。
- 适配器容器:集合,元素可以重复,基于哈希表实现,不排序。
- stack:键值对集合,键可以重复,基于哈希表实现,不排序。
- queue:
- priority_queue:栈,后进先出。
vector扩容如何实现。
:队列,先进先出。vector
:优先队列,元素按优先级出队。
map、set特点,怎么实现的。
map
扩容通常通过创建一个更大的新内存空间,然后将原有元素复制或移动到新空间,之后释放原始内存空间来实现。扩容的大小通常是当前容量的两倍,以减少频繁扩容的开销。
set
map
和 set
的特点是它们存储的元素是自动排序的,
内联函数。
存储键值对,其中键是唯一的,而inline
只存储键且每个键是唯一的。它们是通过平衡二叉搜索树(通常是红黑树)实现的,这个数据结构可以提供对元素的有序存储,以及在对数时间复杂度内进行元素查找、插入和删除操作。
gdb如何debug,怎么传参。
C++内联函数是一种通常用于优化小型、频繁调用的函数的编程技术。通过在函数声明前加gdb <your_program>
关键字,编译器在调用处直接展开函数代码,以减少函数调用的开销。但最终是否内联取决于编译器的决定。
set args <arg1> <arg2> ...
在GDB中进行调试,首先要启动GDB并加载你要调试的程序,可以用 run
命令。传递参数可以在GDB中使用 break
命令来设置命令行参数。然后,你可以使用像 next
来运行程序、
指针的初始化和释放。
设置断点、nullptr
单步执行等命令来控制程序的执行并进行调试。NULL
指针的初始化应该将其设为new
(C++11及以后)或delete
,以指示它尚未指向任何对象。当指针分配了动态内存后(例如,使用delete[]
),完成使用后应使用nullptr
(对于单个对象)或
三个进程都需要读写一块内存,如何调度。
(对于对象数组)来释放分配的内存,然后把指针重新设置为浩鲸云计算C++
,避免悬挂指针的问题。内存分配情况,存放在哪里。
为了协调三个进程对同一块内存区域的读写访问,可以使用互斥锁(mutexes)或读写锁(read-write locks)。互斥锁确保任一时间只有一个进程可以访问该内存。读写锁允许多个读取者同时访问,但写入者独占访问。适当的锁机制需要根据访问模式和性能要求选择。
- 数据区:存放全局变量和静态变量。
- 代码区:存放程序的执行代码。
- 值传递:函数接收参数值的一个副本,原始数据不会被函数修改。
函数参数传递的方式和特点。
static和const的区别。
static
const
static修饰局部变量、全局变量、函数和类各有什么特点。
关键字用来定义静态变量,其生命周期为程序执行期间,但它的作用域限定于定义它的文件或函数内。- 静态函数:其链接属性为内部,只能在定义它的文件内使用。
- 静态类成员:属于类本身,而不是类的任何特定对象,所有对象共享同一个静态成员。
new和malloc,free和delete区别。
new
delete
-
new
和delete
是C++中用于动态内存分配和释放的操作符。malloc
在分配内存的同时调用构造函数初始化对象,free
释放内存前调用对象的析构函数。 -
malloc
和free
是C语言中用于动态内存分配和释放的函数。指针和引用区别。
只分配内存,不初始化, - 指针是一个变量,其值为另一个变量的地址,通过地址,可以直接访问和修改对应内存中的值。 只释放内存,不调用析构函数。
- 指针可以为空,引用必须绑定到一个对象。
- 指针的值(即所指对象的地址)可以改变,但引用一旦与某个对象绑定,就不能再改变引用到其他对象。
深拷贝和浅拷贝。
进程间通信,管道的特点。
浅拷贝指的是复制对象的引用,而不是对象本身,因此原始对象和副本对象共享同一块内存地址。深拷贝则是复制对象及其包含的所有对象,副本和原始对象在内存中完全独立。
- 管道是用于有亲缘关系的进程间通信,如父子进程通信。
- 预处理阶段进行文本替换,不涉及类型检查。
define特点,怎么定义一个a+b的宏。
特点是:
- 宏可以包含参数,但不进行正确性检查。
#define ADD(a, b) ((a) + (b))
(a)
定义一个执行a+b的宏,如下所示:
(b)
这里的
sizeof和strlen区别。
和sizeof
被括起来是为了避免展开时出现的运算优先级问题。strlen
'
是一个编译时运算符,用来得到某个类型或变量在内存中的大小,单位是字节。'
'sprintf、strcpy和memcpy区别。
'
sprintf
是一个运行时函数,用来计算字符串的长度,直到遇到第一个空字符 strcpy
,不包括 memcpy
。
虚函数特点。
- 必须至少有一个函数体,除非声明为纯虚函数。
父类子类构造析构函数调用顺序。
构造析构能否抛出异常,能否是虚函数。
内存对齐。
STL有哪些容器,各自特点。
构造函数调用顺序:首先调用父类构造函数,然后调用子类构造函数。
析构函数调用顺序:首先调用子类析构函数,然后调用父类析构函数。
vector构造函数和析构函数都可以抛出异常,但应谨慎处理,以避免可能导致的资源泄露或不一致状态。
构造函数不能是虚函数,因为虚函数表(vtable)在构造时尚未建立。析构函数可以是虚函数,通常在基类中将析构函数声明为虚析构函数,以确保通过基类指针删除派生类对象时,能正确调用派生类的析构函数。
listC++内存对齐是为了提高内存访问效率,确保数据结构按照某个固定的长度(对齐界限)存储。编译器会自动添加填充字节(padding),使得结构体的每个成员相对于结构体开始位置的偏移量是成员大小或某个特定数值(通常是2的幂)的整数倍。
deque- 关联容器:
- set:动态数组,支持快速随机访问。
- map:双向链表,支持快速插入和删除。
- multiset:双端队列,两端都可以快速插入和删除。
- multimap:
- 无序关联容器(C++11新增):集合,元素唯一且自动排序。
- unordered_set:键值对集合,键唯一且自动排序。
- unordered_map:集合,元素可以重复,自动排序。
- unordered_multiset:键值对集合,键可以重复,自动排序。
- unordered_multimap:
- 适配器容器:集合,元素唯一,基于哈希表实现,不排序。
- stack:键值对集合,键唯一,基于哈希表实现,不排序。
- queue:集合,元素可以重复,基于哈希表实现,不排序。
- priority_queue:键值对集合,键可以重复,基于哈希表实现,不排序。
vector扩容如何实现。
:
vector
:栈,后进先出。map、set特点,怎么实现的。
:队列,先进先出。map
:优先队列,元素按优先级出队。
set
map
扩容通常通过创建一个更大的新内存空间,然后将原有元素复制或移动到新空间,之后释放原始内存空间来实现。扩容的大小通常是当前容量的两倍,以减少频繁扩容的开销。
set
内联函数。
和inline
的特点是它们存储的元素是自动排序的,gdb如何debug,怎么传参。
存储键值对,其中键是唯一的,而gdb <your_program>
只存储键且每个键是唯一的。它们是通过平衡二叉搜索树(通常是红黑树)实现的,这个数据结构可以提供对元素的有序存储,以及在对数时间复杂度内进行元素查找、插入和删除操作。
set args <arg1> <arg2> ...
C++内联函数是一种通常用于优化小型、频繁调用的函数的编程技术。通过在函数声明前加run
关键字,编译器在调用处直接展开函数代码,以减少函数调用的开销。但最终是否内联取决于编译器的决定。
break
在GDB中进行调试,首先要启动GDB并加载你要调试的程序,可以用 next
命令。传递参数可以在GDB中使用
指针的初始化和释放。
命令来设置命令行参数。然后,你可以使用像nullptr
来运行程序、NULL
设置断点、new
单步执行等命令来控制程序的执行并进行调试。delete
指针的初始化应该将其设为delete[]
(C++11及以后)或nullptr
,以指示它尚未指向任何对象。当指针分配了动态内存后(例如,使用
三个进程都需要读写一块内存,如何调度。
),完成使用后应使用(对于单个对象)或(对于对象数组)来释放分配的内存,然后把指针重新设置为,避免悬挂指针的问题。为了协调三个进程对同一块内存区域的读写访问,可以使用互斥锁(mutexes)或读写锁(read-write locks)。互斥锁确保任一时间只有一个进程可以访问该内存。读写锁允许多个读取者同时访问,但写入者独占访问。适当的锁机制需要根据访问模式和性能要求选择。