C

内存

malloc 的底层实现

malloc(size) 的核心功能是在 (heap)区域分配 size 字节的内存,并返回指向该内存块的指针。如果分配失败,返回 NULL

  • 检查请求大小
    • 如果 size == 0,通常返回 NULL 或最小可分配单元。
    • 可能会对 size 进行对齐(比如 8 字节或 16 字节对齐)。
  • 在空闲链表查找合适的块
    • 先查看 空闲链表(free list)是否有合适的块。
    • 如果找到 大小合适 的块,则直接分配。
    • 如果找到的块 比所需的稍大,可能会进行 切割(split)
  • 请求新的内存(如果没有合适的空闲块)
    • 如果空闲链表找不到合适的块,就向 操作系统请求新的内存
    • 通过 sbrk()mmap() 来扩展堆区。
  • 更新管理结构
    • 记录已分配的块(用于 free)。
    • 维护空闲块的信息(用于后续分配)。

在1G内存的计算机中能否malloc(1.2G)?为什么?

malloc() 只是在用户态申请虚拟地址空间,并不立即分配物理内存。

如果虚拟地址空间足够(且有 swap 空间),malloc(1.2G) 可能会成功,但可能会导致 swap 频繁交换,严重影响性能

32位系统中,如果已经有进程占用大量空间malloc(1.2G) 可能因 地址空间不足 而失败。

数组到底存放在哪里

数组的存放位置取决于 数组的定义方式存储类别。在 C/C++ 语言中,数组可能存放在 栈(Stack)、堆(Heap)、数据段(Data/BSS)、代码段(Text) 等不同的内存区域。

  • 栈(Stack)——局部数组,栈内存是 自动分配和释放 的,由系统管理
  • 堆(Heap)——动态分配的数组,手动管理,必须 free(arr) 释放,否则会 内存泄漏
  • 数据段(Data/BSS)——全局或静态数组,
    • global: int global_arr[10]; // BSS 段(未初始化)
    • local: static int static_arr[10]; // BSS 段(未初始化)
    • local: static int static_arr2[10] = {1, 2, 3}; // Data 段(已初始化)
  • 代码段(Text)——字符串常量

C++

左值右值

什么是引用?左、右值引用?

多态

虚函数和纯虚函数的区别

  • 相同:在基类中声明的,使用关键字 virtual。它可以在派生类中被 override
  • 虚函数
    • 可以有具体的实现,可以在派生类中不重写
    • 基类可以实例化
    • 允许通过基类指针或引用来调用派生类中的重写函数,从而实现动态绑定
  • 纯虚函数
    • 没有具体实现(= 0
    • 基类不能实例化
    • 纯虚函数必须在派生类中被重写

内存

malloc + free VS new + delete

malloc + freenew + delete
语言C 语言库函数C++ 操作符
行为调用对象的构造函数和析构函数,支持对象的初始化和销毁; 支持 type 和真数组仅分配和释放原始内存
内存分配分配对象或对象数组的内存,并初始化。分配一块未初始化的内存空间
返回对应对象的指针,类型明确void*, 需要强制类型转换
内存释放调用对象的析构函数来销毁对象(数组使用 delete[]仅释放内存
其他类型安全、面向对象、速度较慢、能抛异常类型无关、原始分配、性能更高、错误返回 NULL 指针

指针

智能指针的种类

智能指针

智能指针的实现

智能指针

  • unique_ptr
    • 独占所有权的智能指针,它确保同一时间内只有一个指向对象
    • 移动转移所有权,不允许复制
  • shared_ptr
    • 允许多个指针共享同一个对象所有权
    • 通过引用计数机制跟踪指向同一个对象的 shared_ptr 实例数量
      • 引用计数降为零时,自动释放资源
      • 引用计数操作是线程安全的
  • weak_ptr
    • 弱引用指针,可访问 shared_ptr 管理的对象,但不增加引用计数

Types

如何避免编译器进行的隐式类型转换: explicit

C++ 有几种类型转换

type

  • 隐式转换
  • 显示转换
    • C 风格转换
      • 不会对这种潜在的错误进行严格的检查
      • double -> int 可能使用截断
    • static_cast: 类型安全的转换,适用于基本数据类型之间、类层次结构中的转换
      • Eg. 在有继承关系的类之间进行向上转型;int -> double
      • double -> intstatic_cast<int>(d)double* -> int* 会报错
    • dynamic_cast: 多态类型转换,通常用于将基类指针或引用转换为派生类指针或引用
      • Eg. 有继承关系的类之间的向下转型
      • 转换失败将会返回 nullptr / 异常
    • const_cast: 添加或删除变量的const属性
      • Eg.
        const int ci = 10;
        int* pi = const_cast<int*>(&ci); // 去掉const属性
    • reinterpret_cast: 低级别的类型转换。将一个类型的**指针(或引用)**转换为另一个类型的指针(或引用),但不会对指针所指向的数据进行任何检查。
      • double -> int 不改变内存内容

STL

STL

C++ vector 底层实现

vector

std::move 原理

move

  • 将传入的左值转换为右值引用

Misc

structclass 的区别

  • struct
    • 默认成员访问权限:public
    • 默认继承权限:public
    • 用于 数据结构POD(Plain Old Data)类型,通常 不包含复杂方法
  • class
    • 默认成员访问权限:private
    • 默认继承权限:private,外部不能直接访问
    • 用于 面向对象编程(OOP),用于 封装成员变量和方法

extern "C" 的作用

用于 C++ 代码调用 C 语言代码,或 让 C 语言调用 C++ 代码。通过 extern "C" 告诉编译器,该函数应按 C 语言规则编译,不进行名称修饰

const 常量和 #define 的区别(编译阶段、安全性、内存占用等)

Ai

对比项const#define
编译阶段编译期处理,有类型检查预处理阶段,简单文本替换
作用域遵循 C++ 作用域规则无作用域,全局替换
安全性有类型检查,防止错误无类型检查,容易出错
内存占用可能优化为单个存储可能导致重复展开
可调试性变量有名字,调试器可见预处理后消失,调试器不可见
是否能修改不可修改,防止错误只是文本替换,容易被意外改动

const 适用场景

  • 需要 类型检查,防止错误。
  • 需要 节省内存,减少重复定义。
  • 需要 可调试性,在调试器中可见。

#define 适用场景

  • 定义 宏函数,如 #define SQUARE(x) ((x)*(x))
  • 定义 条件编译#ifdef / #ifndef)。
  • 定义 跨文件的编译开关

没有指向引用的指针,因为引用是没有地址的,但是有指针的引用