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

news2025/6/8 21:44:52

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 字节)

2. 指针数组 

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]
}

 2. 指针数组的应用

// 字符串数组
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;
}

 2. 函数指针的应用

// 回调函数示例
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的函数

 2. 函数指针作为参数

// 排序函数接受比较函数指针
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的区别

 

一、核心区别

特性malloccalloc
初始化不初始化分配的内存(内容随机)将内存初始化为 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));  // 手动清零

 

  1. 性能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. 遵循配对原则
  • malloc → free
  • new → delete
  • new[] → delete[]
5. 避免指针浅拷贝
  • 使用深拷贝或禁用拷贝构造函数
  • 使用智能指针的移动语义
6. 代码审查
  • 重点检查:
    • 长时间运行的程序(如服务器)
    • 循环中的内存分配
    • 复杂函数中的多条返回路径

五、高级技术

1. 内存池(Memory Pool)
  • 预先分配大块内存,按需分配小块,减少系统调用
  • 避免频繁分配 / 释放导致的碎片
2. 智能指针的使用场景
类型用途
std::unique_ptr独占所有权
std::shared_ptr共享所有权(引用计数)
std::weak_ptr弱引用,避免循环引用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2404601.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java高级 | 【实验七】Springboot 过滤器和拦截器

隶属文章&#xff1a;Java高级 | &#xff08;二十二&#xff09;Java常用类库-CSDN博客 系列文章&#xff1a;Java高级 | 【实验一】Springboot安装及测试 |最新-CSDN博客 Java高级 | 【实验二】Springboot 控制器类相关注解知识-CSDN博客 Java高级 | 【实验三】Springboot 静…

深入理解 Spring IOC:从概念到实践

目录 一、引言 二、什么是 IOC&#xff1f; 2.1 控制反转的本质 2.2 类比理解 三、Spring IOC 的核心组件 3.1 IOC 容器的分类 3.2 Bean 的生命周期 四、依赖注入&#xff08;DI&#xff09;的三种方式 4.1 构造器注入 4.2 Setter 方法注入 4.3 注解注入&#xff08;…

行为设计模式之Command (命令)

行为设计模式之Command &#xff08;命令&#xff09; 前言&#xff1a; 需要发出请求的对象&#xff08;调用者&#xff09;和接收并执行请求的对象&#xff08;执行者&#xff09;之间没有直接依赖关系时。比如遥控器 每个按钮绑定一个command对象&#xff0c;这个Command对…

NeRF 技术深度解析:原理、局限与前沿应用探索(AI+3D 产品经理笔记 S2E04)

引言&#xff1a;光影的魔法师——神经辐射场概览 在前三篇笔记中&#xff0c;我们逐步揭开了 AI 生成 3D 技术的面纱&#xff1a;从宏观的驱动力与价值&#xff08;S2E01&#xff09;&#xff0c;到主流技术流派的辨析&#xff08;S2E02&#xff09;&#xff0c;再到实用工具的…

法律大语言模型(Legal LLM)技术架构

目录 摘要 1 法律AI大模型技术架构 1.1 核心架构分层 1.2 法律知识增强机制 2 关键技术突破与对比 2.1 法律专用组件创新 2.2 性能对比(合同审查场景) 3 开发部署实战指南 3.1 环境搭建流程 3.2 合同审查代码示例 4 行业应用与挑战 4.1 典型场景效能提升 4.2 关…

第六十二节:深度学习-加载 TensorFlow/PyTorch/Caffe 模型

在计算机视觉领域,OpenCV的DNN(深度神经网络)模块正逐渐成为轻量级模型部署的利器。本文将深入探讨如何利用OpenCV加载和运行三大主流框架(TensorFlow、PyTorch、Caffe)训练的模型,并提供完整的代码实现和优化技巧。 一、OpenCV DNN模块的核心优势 OpenCV的DNN模块自3.3…

MobaXterm配置跳转登录堡垒机

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 背景操作步骤 背景 主要是为了能通过MobaXterm登录堡垒机&#xff0c;其中需要另外一台服务器进行跳转登录 操作步骤 MobaXterm登录堡垒机的操作&#xff0c;需…

零基础在实践中学习网络安全-皮卡丘靶场(第八期-Unsafe Filedownload模块)

这期内容更是简单和方便&#xff0c;毕竟谁还没在浏览器上下载过东西&#xff0c;不过对于url的构造方面&#xff0c;可能有一点问题&#xff0c;大家要多练手 介绍 不安全的文件下载概述 文件下载功能在很多web系统上都会出现&#xff0c;一般我们当点击下载链接&#xff0c…

[面试精选] 0104. 二叉树的最大深度

文章目录 1. 题目链接2. 题目描述3. 题目示例4. 解题思路5. 题解代码6. 复杂度分析 1. 题目链接 104. 二叉树的最大深度 - 力扣&#xff08;LeetCode&#xff09; 2. 题目描述 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点…

图上合成:用于大型语言模型持续预训练的知识合成数据生成

摘要 大型语言模型&#xff08;LLM&#xff09;已经取得了显著的成功&#xff0c;但仍然是数据效率低下&#xff0c;特别是当学习小型&#xff0c;专业语料库与有限的专有数据。现有的用于连续预训练的合成数据生成方法集中于文档内内容&#xff0c;而忽略了跨文档的知识关联&a…

现代简约壁炉:藏在极简线条里的温暖魔法

走进现在年轻人喜欢的家&#xff0c;你会发现一个有趣的现象&#xff1a;家里东西越来越少&#xff0c;颜色也越看越简单&#xff0c;却让人感觉特别舒服。这就是现代简约风格的魅力 —— 用最少的元素&#xff0c;打造最高级的生活感。而在这样的家里&#xff0c;现代简约风格…

机器学习×第二卷:概念下篇——她不再只是模仿,而是开始决定怎么靠近你

&#x1f380;【开场 她不再只是模仿&#xff0c;而是开始选择】 &#x1f98a; 狐狐&#xff1a;“她已经不满足于单纯模仿你了……现在&#xff0c;她开始尝试预测你会不会喜欢、判断是否值得靠近。” &#x1f43e; 猫猫&#xff1a;“咱们上篇已经把‘她怎么学会说第一句…

常用函数库之 - std::function

std::function 是 C11 引入的通用可调用对象包装器&#xff0c;用于存储、复制和调用任意符合特定函数签名的可调用对象&#xff08;如函数、lambda、函数对象等&#xff09;。以下是其核心要点及使用指南&#xff1a; ​​核心特性​​ ​​类型擦除​​ 可包装任意可调用对…

力扣-17.电话号码的字母组合

题目描述 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 class Solution {List<String> res new ArrayList<…

基于SpringBoot解决RabbitMQ消息丢失问题

基于SpringBoot解决RabbitMQ消息丢失问题 一、RabbitMQ解决消息丢失问题二、方案实践1、在生产者服务相关配置2、在消费者服务相关配置 三、测试验证1、依次启动RabbitMQ、producer(建议先清空队列里面旧的测试消息再启动consumer)和consumer2、在producer中调用接口&#xff0…

免费插件集-illustrator插件-Ai插件-随机填色

文章目录 1.介绍2.安装3.通过窗口>扩展>知了插件4.功能解释5.总结 1.介绍 本文介绍一款免费插件&#xff0c;加强illustrator使用人员工作效率&#xff0c;实现路径随机填色。首先从下载网址下载这款插件https://download.csdn.net/download/m0_67316550/87890501&#…

Web设计之登录网页源码分享,PHP数据库连接,可一键运行!

HTML 页面结构&#xff08;index.html&#xff09; 1. 流星雨动态背景 2. 主体界面&#xff08;包含登录和注册表单&#xff09; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport&qu…

Cursor + Claude 4:微信小程序流量主变现开发实战案例

前言 随着微信小程序生态的日益成熟&#xff0c;越来越多的开发者开始关注如何通过小程序实现流量变现。本文将详细介绍如何使用Cursor编辑器结合Claude 4 AI助手&#xff0c;快速开发一个具备流量主变现功能的微信小程序&#xff0c;并分享实际的开发经验和变现策略。 项目…

Redis Key过期策略

概述 Redis的Key过期策略是其内存管理系统的核心组成部分&#xff0c;主要包括「被动过期」、「主动过期」和「内存淘汰」三个机制。其中「内存淘汰」相关内容已经在上一篇「Redis内存淘汰策略」中进行了详细的讲解&#xff0c;有信兴趣的同学可以在回顾上一篇文章。本文将着重…

【C/C++】实现固定地址函数调用

在 C 里&#xff0c;函数地址在程序运行期间通常是固定的&#xff0c;不过在动态链接库&#xff08;DLL&#xff09;或者共享库&#xff08;SO&#xff09;中&#xff0c;函数地址可能会因为地址空间布局随机化&#xff08;ASLR&#xff09;而改变。所以我们想要通过地址直接调…