嵌入式面试高频(5)!!!C++语言(嵌入式八股文,嵌入式面经)

一**、**C++有几种传值方式之间的区别

一、值传递(Pass by Value)

  • 机制:创建参数的副本,函数内操作不影响原始数据
  • 语法void func(int x)
  • 特点
    • 数据安全:原始数据不受影响

    • 性能开销:需要复制大对象(如结构体、类)

    • 示例

      void increment(int x) { x++; } // 修改副本,不影响原始值
      int a = 10;
      increment(a); // a仍为10引用高效移动资源

二、指针传递(Pass by Pointer)

  • 机制:传递变量的地址,函数通过指针间接操作原始数据
  • 语法void func(int* ptr)
  • 特点
    • 可修改原始数据 :通过*ptr修改

    • 空指针风险 :需检查ptr != nullptr

    • 示例

      void increment(int* ptr) {
      if (ptr) (*ptr)++; // 安全检查
      }
      int a = 10;
      increment(&a); // a变为11

三、引用传递(Pass by Reference)

  • 机制:传递变量的别名,函数直接操作原始数据
  • 语法void func(int& ref)
  • 特点
    • 可修改原始数据:直接操作引用

    • 安全性:引用必须初始化,无空引用

    • 示例

      void increment(int& ref) { ref++; } // 直接操作原始值
      int a = 10;
      increment(a); // a变为11

四、核心区别对比

特性 值传递 指针传递 引用传递
操作对象 副本 原始数据(通过地址) 原始数据(通过别名)
是否修改原值
语法复杂度 简单(直接传值) 较复杂(需解引用) 简单(类似值传递)
空值风险 有空指针风险 无(必须初始化)
典型用途 简单数据、只读操作 需显式传递地址、可空 对象参数、避免拷贝

五、示例对比

复制代码
// 值传递
void passByValue(int val) { val = 20; } // 不影响原始值

// 指针传递
void passByPointer(int* ptr) { 
    if (ptr) *ptr = 20; // 需检查空指针
}

// 引用传递
void passByReference(int& ref) { ref = 20; } // 直接修改

int main() {
    int x = 10;
    
    passByValue(x);     // x仍为10
    passByPointer(&x);  // x变为20
    passByReference(x); // x变为20
    
    return 0;
}

六、C++11 新增:右值引用(Move Semantics)

  • 机制:专门处理临时对象(右值)的引用,避免深拷贝

  • 语法void func(Type&& rvalue)

  • 典型用途:移动构造函数、移动赋值运算符

  • 示例

    std::vector<int> createVector() {
    return std::vector<int>{1,2,3};
    }

    std::vector<int> vec = createVector(); // 通过右值引用高效移动资源

二.数组指针与指针数组的区别

一、核心区别

特性 数组指针(Pointer to Array) 指针数组(Array of Pointers)
本质 指针:指向一个数组 数组:存储多个指针
语法 int (*ptr)[5];(括号强制 ptr 为指针) int* arr[5];(arr 先与 [] 结合为数组)
指向对象 整个数组 数组的元素(每个元素是一个指针)
指针运算 ptr + 1 跳过整个数组(如 5 个 int) arr + 1 指向下一个元素(下一个指针)
典型用途 传递多维数组、精确控制内存布局 管理多个动态分配的对象、字符串数组

二、语法对比

  1. 数组指针(指向数组的指针)

    int arr[5] = {1,2,3,4,5};
    int (*ptr)[5] = &arr; // 指向包含5个int的数组

    // 访问元素
    (*ptr)[0] = 10; // 修改arr[0]为10

  2. 指针数组(包含指针的数组)

    int a = 1, b = 2, c = 3;
    int* arr[3] = {&a, &b, &c}; // 数组的每个元素是int*

    // 访问元素
    *arr[0] = 10; // 修改a为10

三、内存布局差异

  1. 数组指针

    ptr ──> [1, 2, 3, 4, 5] // 指向整个数组

  • ptr 存储整个数组的起始地址
  • sizeof(ptr) 为指针大小(通常 4/8 字节)
  1. 指针数组

    arr ──> [&a, &b, &c] // 数组元素为指针
    │ │ │
    ▼ ▼ ▼
    a b c

  • arr 是一个数组,包含多个指针
  • sizeof(arr)3 * sizeof(int*)

四、典型应用场景

1. 数组指针的应用
复制代码
// 传递多维数组
void printMatrix(int (*matrix)[4], int rows) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < 4; j++) {
            printf("%d ", matrix[i][j]);
        }
        printf("\n");
    }
}

int main() {
    int matrix[3][4] = {...};
    printMatrix(matrix, 3);  // matrix退化为int (*)[4]
}
  1. 指针数组的应用

    // 字符串数组
    const char* fruits[3] = {
    "Apple",
    "Banana",
    "Cherry"
    };

    // 动态内存管理
    int* ptrs[5];
    for (int i = 0; i < 5; i++) {
    ptrs[i] = new int(i);
    }

五、常见混淆点

1. 括号位置决定类型
复制代码
int (*ptr)[5];  // 数组指针:ptr是指向包含5个int的数组的指针
int* ptr[5];    // 指针数组:ptr是包含5个int*的数组
2. 数组名 vs 数组指针
复制代码
int arr[5];
int* ptr1 = arr;      // 指向首元素的指针(隐式转换)
int (*ptr2)[5] = &arr; // 指向整个数组的指针

printf("%p\n", arr);     // 数组首元素地址
printf("%p\n", &arr);    // 整个数组的地址(数值相同,但类型不同)
printf("%p\n", arr + 1); // 跳过1个元素
printf("%p\n", &arr + 1); // 跳过整个数组(5个元素)

三.指针函数与函数指针的区别

一、核心区别

特性 指针函数(Function Returning Pointer) 函数指针(Pointer to Function)
本质 函数:返回值为指针类型 指针:指向一个函数
语法 int* func(int a);(返回 int*) int (*ptr)(int a);(ptr 为指针)
用途 返回动态分配的内存或全局变量地址 作为参数传递函数、实现回调机制
调用方式 int* result = func(10); int val = (*ptr)(10);ptr(10);

二、语法对比

1. 指针函数(返回指针的函数)
复制代码
int* createArray(int size) {
    int* arr = new int[size];
    for (int i = 0; i < size; i++) {
        arr[i] = i;
    }
    return arr;  // 返回动态分配的数组指针
}

// 调用
int* ptr = createArray(5);
2. 函数指针(指向函数的指针)
复制代码
int add(int a, int b) { return a + b; }

// 定义函数指针并初始化
int (*op)(int, int) = add;

// 调用方式1
int result = (*op)(3, 4);  // 显式解引用

// 调用方式2(C++允许隐式解引用)
int result2 = op(3, 4);    // 等价于上一行

三、典型应用场景

1. 指针函数的应用
复制代码
// 返回静态变量的地址
const char* getMessage() {
    static const char* msg = "Hello";
    return msg;
}
  1. 函数指针的应用

    // 回调函数示例
    void process(int a, int b, int (*func)(int, int)) {
    int result = func(a, b);
    printf("Result: %d\n", result);
    }

    int main() {
    int (*add)(int, int) = [](int a, int b) { return a + b; };
    process(3, 4, add); // 输出7
    }

四、常见混淆点

1. 括号位置决定类型
复制代码
int* func(int a);  // 指针函数:返回int*
int (*ptr)(int a); // 函数指针:ptr指向返回int的函数
  1. 函数指针作为参数

    // 排序函数接受比较函数指针
    void sort(int* arr, int size, bool (*compare)(int, int)) {
    // 排序逻辑...
    }

    bool ascending(int a, int b) { return a < b; }

    // 调用
    sort(array, 10, ascending);

四.malloc和calloc的区别

一、核心区别

特性 malloc calloc
初始化 不初始化分配的内存(内容随机) 将内存初始化为 0
参数 单个参数:所需内存字节数 两个参数:元素数量和元素大小
原型 void* malloc(size_t size); void* calloc(size_t num, size_t size);
性能 略快(无需初始化) 略慢(需清零内存)

二、示例对比

1. malloc 的使用
复制代码
int* ptr = (int*)malloc(5 * sizeof(int));  // 分配5个int的内存
if (ptr != NULL) {
    // 内存内容未初始化,可能包含随机值
    for (int i = 0; i < 5; i++) {
        printf("%d ", ptr[i]);  // 输出随机值
    }
}
2. calloc 的使用
复制代码
int* ptr = (int*)calloc(5, sizeof(int));  // 分配5个int的内存并初始化为0
if (ptr != NULL) {
    // 内存内容已初始化为0
    for (int i = 0; i < 5; i++) {
        printf("%d ", ptr[i]);  // 输出: 0 0 0 0 0
    }
}

三、内存布局差异

复制代码
// malloc分配的内存(未初始化)
ptr ──> [随机值][随机值][随机值][随机值][随机值]

// calloc分配的内存(初始化为0)
ptr ──> [0][0][0][0][0]

四、安全与性能考量

  1. 安全性

    • calloc适合需要初始化的场景(如存储结构体、数组)
    • malloc需手动初始化(如使用memset):

    int* ptr = malloc(5 * sizeof(int));
    memset(ptr, 0, 5 * sizeof(int)); // 手动清零

  2. 性能calloc因初始化操作会稍慢

    • 大数据块初始化可能影响性能

五、典型应用场景

场景 推荐函数 原因
存储需要初始化的数据 calloc 自动清零,避免未定义行为
存储无需初始化的数据 malloc 略高效
分配二进制缓冲区 malloc 后续会写入数据,无需提前初始化
分配结构体数组 calloc 确保结构体成员初始化为有效值

五.内存泄漏,如何检测和避免?

一、什么是内存泄漏?

  • 定义 :程序动态分配的内存(如malloc/new)未被正确释放(如free/delete),导致这部分内存永久无法被回收
  • 危害
    • 随着程序运行,可用内存逐渐减少
    • 最终导致系统性能下降、程序崩溃或系统崩溃

二、内存泄漏的常见原因

  1. 忘记释放内存

    void func() {
    int* ptr = new int[100]; // 分配内存
    // 忘记调用delete[] ptr;
    }

异常导致路径未释放

复制代码
void func() {
    int* ptr = new int[100];
    if (condition) {
        throw std::exception();  // 异常退出,未释放内存
    }
    delete[] ptr;
}

指针覆盖

复制代码
int* ptr = new int;
ptr = new int;  // 原内存丢失,无法释放

循环分配内存

复制代码
while (true) {
    int* ptr = new int[1000];  // 持续分配,无释放
}

类中未定义析构函数

复制代码
class Resource {
public:
    Resource() { data = new int[100]; }
    // 未定义析构函数释放data
private:
    int* data;
};

三、检测内存泄漏的方法

1. 静态代码分析工具
  • 工具:Cppcheck、Clang-Tidy、PC-Lint
  • 特点
    • 不运行程序,直接分析代码
    • 检测常见模式(如分配后未释放)
  • 示例命令

cppcheck --enable=all --inconclusive your_file.cpp

2. 动态内存分析工具
  • Valgrind(Linux)

  • valgrind --leak-check=full ./your_program

四、避免内存泄漏的最佳实践

1. RAII(资源获取即初始化)原则
  • 使用智能指针(C++): cpp

    运行

    复制代码
    #include <memory>
    
    void func() {
        std::unique_ptr<int[]> ptr(new int[100]);  // 自动释放
        // 无需手动delete
    }
2. 容器替代原始数组

cpp

运行

复制代码
#include <vector>

void func() {
    std::vector<int> data(100);  // 自动管理内存
}
3. 异常安全
  • 使用try-catch确保资源释放:

    cpp

    运行

    复制代码
    void func() {
        int* ptr = new int[100];
        try {
            // 可能抛出异常的代码
        } catch (...) {
            delete[] ptr;
            throw;
        }
        delete[] ptr;
    }
4. 遵循配对原则
  • mallocfree
  • newdelete
  • new[]delete[]
5. 避免指针浅拷贝
  • 使用深拷贝或禁用拷贝构造函数
  • 使用智能指针的移动语义
6. 代码审查
  • 重点检查:
    • 长时间运行的程序(如服务器)
    • 循环中的内存分配
    • 复杂函数中的多条返回路径

五、高级技术

1. 内存池(Memory Pool)
  • 预先分配大块内存,按需分配小块,减少系统调用
  • 避免频繁分配 / 释放导致的碎片
2. 智能指针的使用场景
类型 用途
std::unique_ptr 独占所有权
std::shared_ptr 共享所有权(引用计数)
std::weak_ptr 弱引用,避免循环引用
相关推荐
星辰pid27 分钟前
STM32实现四自由度机械臂(SG90舵机)多功能控制(软件篇freertos)
stm32·单片机·嵌入式硬件·机械臂
山登绝顶我为峰 3(^v^)32 小时前
如何录制带备注的演示文稿(LaTex Beamer + Pympress)
c++·线性代数·算法·计算机·密码学·音视频·latex
十五年专注C++开发5 小时前
CMake基础:条件判断详解
c++·跨平台·cmake·自动化编译
森焱森6 小时前
水下航行器外形分类详解
c语言·单片机·算法·架构·无人机
QuantumStack7 小时前
【C++ 真题】P1104 生日
开发语言·c++·算法
天若有情6738 小时前
01_软件卓越之道:功能性与需求满足
c++·软件工程·软件
小殷学长8 小时前
【单片机毕业设计17-基于stm32c8t6的智能倒车监测系统】
stm32·单片机·课程设计
whoarethenext8 小时前
使用 C++/OpenCV 和 MFCC 构建双重认证智能门禁系统
开发语言·c++·opencv·mfcc
Jay_5159 小时前
C++多态与虚函数详解:从入门到精通
开发语言·c++
TESmart碲视9 小时前
HKS201-M24 大师版 8K60Hz USB 3.0 适用于 2 台 PC 1台显示器 无缝切换 KVM 切换器
单片机·嵌入式硬件·物联网·游戏·计算机外设·电脑·智能硬件