字节跳动面经


1. vector 和 list 的区别?

  • vector 是内存连续的线性结构,支持随机访问,插入/删除(非末尾)效率低(需移动元素)。

  • list 是内存不连续的链表结构,不支持随机访问,但任意位置插入/删除效率高(O(1))。

  • 遍历时 vector 因内存连续(缓存友好)性能更高,list 易导致 Cache Miss。


2. 进程和线程的区别?

  • 进程是资源分配 的基本单位,线程是CPU调度的基本单位。

  • 进程间内存隔离,线程共享堆、全局变量、代码段,但各有独立栈和寄存器上下文。

  • 线程切换开销小(仅保存寄存器),进程切换需更新页表等,开销大。

  • 进程间通信需 IPC(管道、消息队列等),线程可直接通过共享内存通信。

  • 一个线程崩溃可能导致整个进程崩溃,进程间则相互独立。


3. 智能指针和循环引用?

  • 智能指针是封装原生指针的栈对象 ,指向堆内存,内部含对象指针控制块指针(含引用计数等)。

  • 循环引用:如 shared_ptr<A>shared_ptr<B>相互指向,导致引用计数永不为0,内存泄漏。

  • 解决:用 weak_ptr替代一方,避免增加引用计数。


4. 智能指针的两个区域

  • 对象区域:指向实际堆内存资源。

  • 控制块区域:指向堆中的控制块,包含强引用计数、弱引用计数、删除器等。


5. 信号量

  • 本质是原子计数器,用于控制访问共享资源的线程数量,达到值时阻塞后续线程。

6. 互斥锁、读写锁、自旋锁

  • 互斥锁:线程未获锁时被挂起(休眠)。

  • 读写锁:读锁可共享,写锁独占。

  • 自旋锁:线程未获锁时循环尝试(忙等),不挂起,适合短时间等待。


7. TCP 三次握手

  • 过程:SYN → SYN-ACK → ACK。

  • 服务端:收到 SYN 放入半连接队列 ,收到 ACK 后移入全连接队列


8. map 和 unordered_map 的区别

  • map:基于红黑树,有序,查找 O(logN)。

  • unordered_map:基于哈希表,平均 O(1),无序,需处理哈希冲突。


9. 继承和多态

  • 继承:实现代码复用。

  • 多态:动态多态通过虚函数重写实现,统一接口;动态绑定在运行时确定调用函数。


10. 内存空间布局

  • 自低地址到高地址:

    1. 代码区、常量区、静态区(BSS、Data)

    2. 堆区(向上增长)

    3. 共享库映射区

    4. 栈区(向下增长)

    5. 内核空间

  • 堆和栈大小动态变化,中间有共享库区。


11. C++ 内存分布:栈区 vs 堆区

  • 栈区:局部变量、函数参数、返回地址(自动管理)。

  • 堆区:malloc/new动态分配,手动管理生命周期。


12. C++ 从源程序到可执行文件的过程

  1. 预处理 :处理宏、头文件、条件编译等(.cpp.i)。

  2. 编译 :将源代码转为汇编(.i.s)。

  3. 汇编 :将汇编转为机器码(.s.o)。

  4. 链接:合并多个目标文件及库,生成可执行文件。


13. 多线程同步方式

  • 互斥锁、读写锁、自旋锁、信号量、条件变量、屏障等。

14. sizeof在编译期还是运行期确定?

  • 固定大小类型(如基本类型、结构体)在编译期确定。

  • 对**可变长度数组(C99)**​ 在运行时计算,但 C++ 不支持变长数组。


15. 函数重载机制及确定时机

  • 重载依赖函数名、参数类型/数量/顺序,在编译期确定(名字改编)。

  • 虚函数重写(多态)在运行期通过虚函数表确定。


16. 常量指针和指针常量

  • 常量指针const int* pint const* p,指针指向的内容不可变。

  • 指针常量int* const p,指针本身(指向)不可变。


17. vector 原理及扩容

  • 内部维护三个指针:startfinishend_of_storage

  • 当容量不足时,按一定比例(如 1.5 或 2 倍)重新分配内存,复制原数据到新空间。


18. 引用和指针的区别

  • 引用必须初始化且不能为空,指针可以为空。

  • 引用不能改变指向,指针可以。

  • 引用是别名,指针是独立变量存储地址。


19. auto遍历容器

  • 传统遍历:for (auto it = vec.begin(); it != vec.end(); ++it)需手动 *it解引用。

  • 范围 for 循环:for (auto& val : vec)自动解引用。


20. static_castdynamic_cast

  • static_cast:编译期转换,用于相关类型(如数值类型、基类转派生类),不进行运行时检查。

  • dynamic_cast :运行期检查,用于多态类型(需虚函数),安全向下转换,失败返回 nullptr


21. inline#define

  • inline:编译期展开,有类型检查,可调试。

  • #define:预处理文本替换,无类型检查,难调试。


22. newmalloc的区别

  • new是运算符,自动计算大小,调用构造函数,返回类型指针。

  • malloc是函数,需手动指定字节数,不调用构造函数,返回 void*


23. 如何防止头文件被多次包含?

  1. #pragma once(非标准但广泛支持)。

  2. 条件编译

    复制代码
    #ifndef HEADER_NAME
    #define HEADER_NAME
    // 头文件内容
    #endif

24. 父子进程匿名管道读写安全

  • 管道是内核 FIFO 队列,保证读写顺序(先写先读)。

  • 读写原子性:单次写操作 ≤ 4KB 是原子的。

  • 无数据时读阻塞,满时写阻塞。


25. 类中能调用同类对象的私有方法吗?

  • 可以,同类的不同对象可访问彼此的私有成员(封装是针对类而非对象)。

26. C++ 内存管理及常见问题

  • 分区:栈、堆、静态/全局、常量、代码区。

  • 管理方式:new/deletemalloc/free、智能指针。

  • 常见问题:

    • 内存泄漏

    • 野指针/悬垂指针

    • 重复释放

    • 内存碎片


27. 堆栈内存常见问题

  • :溢出(递归过深、局部变量过大)。

    • 内存泄漏

    • 越界访问

    • 使用已释放内存

    • 碎片化


28. 派生类中调用父类同名方法

复制代码
class Base {
public:
    void func() {}
};
class Derived : public Base {
public:
    void func() {
        Base::func();  // 显式调用父类版本
    }
};

29. 派生类中定义父类指针

复制代码
class Base { /* ... */ };
class Derived : public Base { /* ... */ };

Base* ptr = new Derived();  // 父类指针指向派生类对象

30. volatile的作用

  • 防止编译器优化对变量的读写(如多线程、硬件寄存器访问),确保每次访问都从内存读取。

31. 僵尸进程和孤儿进程

  • 僵尸进程 :子进程结束但父进程未回收(wait/waitpid),占用进程表项。

  • 孤儿进程:父进程先结束,子进程被 init/systemd 进程(PID=1)接管并回收。

  • 危害:僵尸进程过多导致进程表满,孤儿进程无害(会被系统回收)。


相关推荐
天才奇男子20 小时前
HAProxy高级功能全解析
linux·运维·服务器·微服务·云原生
学嵌入式的小杨同学21 小时前
【Linux 封神之路】信号编程全解析:从信号基础到 MP3 播放器实战(含核心 API 与避坑指南)
java·linux·c语言·开发语言·vscode·vim·ux
酥暮沐21 小时前
iscsi部署网络存储
linux·网络·存储·iscsi
❀͜͡傀儡师21 小时前
centos 7部署dns服务器
linux·服务器·centos·dns
Dying.Light1 天前
Linux部署问题
linux·运维·服务器
S19011 天前
Linux的常用指令
linux·运维·服务器
小义_1 天前
【RH134知识点问答题】第7章 管理基本存储
linux·运维·服务器
梁洪飞1 天前
内核的schedule和SMP多核处理器启动协议
linux·arm开发·嵌入式硬件·arm
_运维那些事儿1 天前
VM环境的CI/CD
linux·运维·网络·阿里云·ci/cd·docker·云计算
Y1rong1 天前
linux之文件IO
linux