嵌入式面试高频(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仍为10passByPointer(&x); // x变为20passByReference(x); // x变为20return 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的区别
一、核心区别
特性 | 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) {// 内存内容已初始化为0for (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]
四、安全与性能考量
-
安全性:
calloc
适合需要初始化的场景(如存储结构体、数组)malloc
需手动初始化(如使用memset
):
int* ptr = malloc(5 * sizeof(int));
memset(ptr, 0, 5 * sizeof(int)); // 手动清零
-
性能:
calloc
因初始化操作会稍慢- 大数据块初始化可能影响性能
五、典型应用场景
场景 | 推荐函数 | 原因 |
---|---|---|
存储需要初始化的数据 | calloc | 自动清零,避免未定义行为 |
存储无需初始化的数据 | malloc | 略高效 |
分配二进制缓冲区 | malloc | 后续会写入数据,无需提前初始化 |
分配结构体数组 | calloc | 确保结构体成员初始化为有效值 |
五.内存泄漏,如何检测和避免?
一、什么是内存泄漏?
- 定义:程序动态分配的内存(如
malloc
/new
)未被正确释放(如free
/delete
),导致这部分内存永久无法被回收 - 危害:
- 随着程序运行,可用内存逐渐减少
- 最终导致系统性能下降、程序崩溃或系统崩溃
二、内存泄漏的常见原因
-
忘记释放内存:
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 | 弱引用,避免循环引用 |
相关文章:
嵌入式面试高频(5)!!!C++语言(嵌入式八股文,嵌入式面经)
一、C有几种传值方式之间的区别 一、值传递(Pass by Value) 机制:创建参数的副本,函数内操作不影响原始数据语法:void func(int x)特点: 数据安全:原始数据不受影响性能开销:需要复…...
C++动态规划-线性DP
这是一套C线性DP题目的答案。如果需要题目,请私信我,我将会更新题干 P1:单子序列最大和 #include <bits/stdc.h> using namespace std; int n,A,B,C; int a[200005]; int s[200005]; int main() {ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)…...

Java高级 | 【实验七】Springboot 过滤器和拦截器
隶属文章:Java高级 | (二十二)Java常用类库-CSDN博客 系列文章:Java高级 | 【实验一】Springboot安装及测试 |最新-CSDN博客 Java高级 | 【实验二】Springboot 控制器类相关注解知识-CSDN博客 Java高级 | 【实验三】Springboot 静…...
es地理信息索引的类型以及geo_point和geo_hash的关系
Elasticsearch中地理信息索引的主要数据类型有两种: geo_point:用于存储单个地理点坐标(如纬度/经度),支持精确位置查询和基于距离的搜索操作。geo_shape:用于存储复杂的地理形状(如点、线、多…...

深入理解 Spring IOC:从概念到实践
目录 一、引言 二、什么是 IOC? 2.1 控制反转的本质 2.2 类比理解 三、Spring IOC 的核心组件 3.1 IOC 容器的分类 3.2 Bean 的生命周期 四、依赖注入(DI)的三种方式 4.1 构造器注入 4.2 Setter 方法注入 4.3 注解注入(…...
Vue解决开发环境 Ajax 跨域问题
一、前言 在使用 Vue 进行前后端分离开发时,前端通常运行在本地开发服务器(如 http://localhost:8080),而后端接口可能部署在其他域名或端口下(如 http://api.example.com:3000)。这时就可能出现 跨域&…...

行为设计模式之Command (命令)
行为设计模式之Command (命令) 前言: 需要发出请求的对象(调用者)和接收并执行请求的对象(执行者)之间没有直接依赖关系时。比如遥控器 每个按钮绑定一个command对象,这个Command对…...
若依添加添加监听容器配置(删除键,键过期)
1、配置Redis的键触发事件 # 基础配置 bind 0.0.0.0 # 允许所有IP连接 protected-mode no # 关闭保护模式(生产环境建议结合密码使用) port 6379 # 默认端口 daemonize no …...

NeRF 技术深度解析:原理、局限与前沿应用探索(AI+3D 产品经理笔记 S2E04)
引言:光影的魔法师——神经辐射场概览 在前三篇笔记中,我们逐步揭开了 AI 生成 3D 技术的面纱:从宏观的驱动力与价值(S2E01),到主流技术流派的辨析(S2E02),再到实用工具的…...
ROS2,工作空间中新建了一个python脚本,需要之后作为节点运行。告诉我步骤?
提问 ROS2,工作空间中新建了一个python脚本,需要之后运行。告诉我步骤? 大概要包括而不限于:chmod给可执行权限、setup.py中entry point的配置,如果在launch文件中要使用,还涉及到launch.py文件的配置。最…...
【AI智能体】Spring AI MCP 从使用到操作实战详解
目录 一、前言 二、MCP 介绍 2.1 什么是MCP 2.2 MCP 核心特点 2.3 MCP 核心价值 2.4 MCP 与Function Calling 区别 三、Spring AI MCP 架构介绍 3.1 整体架构 3.1.1 三层架构实现说明 3.2 服务端与客户端 3.2.1 MCP 服务端 3.2.1 MCP 客户端 3.3 MCP中SSE和STDIO区…...
Vue:Ajax
AJAX 允许我们在不刷新页面的情况下与服务器交互,实现:动态加载数据,提交表单信息,实时更新内容,与后端 API 通信。通常使用专门的 HTTP 客户端库来处理 AJAX 请求。 npm install axiosimport axios from axios;expor…...

法律大语言模型(Legal LLM)技术架构
目录 摘要 1 法律AI大模型技术架构 1.1 核心架构分层 1.2 法律知识增强机制 2 关键技术突破与对比 2.1 法律专用组件创新 2.2 性能对比(合同审查场景) 3 开发部署实战指南 3.1 环境搭建流程 3.2 合同审查代码示例 4 行业应用与挑战 4.1 典型场景效能提升 4.2 关…...
理解 RAG_HYBRID_BM25_WEIGHT:打造更智能的混合检索增强生成系统
目录 理解 RAG_HYBRID_BM25_WEIGHT:打造更智能的混合检索增强生成系统 一、什么是 Hybrid RAG? 二、什么是 RAG_HYBRID_BM25_WEIGHT? 三、参数设置示例 四、什么时候该调整它? 五、实战建议 六、总结 理解 RAG_HYBRID_BM25…...
Hive终极性能优化指南:从原理到实战
摘要:本文系统总结Hive在生产环境的核心调优手段,涵盖执行引擎选择、存储优化、SQL技巧、资源调配及数据倾斜解决方案,附可复用的参数配置与实战案例。 一、执行引擎优化:突破MapReduce瓶颈 启用Tez/Spark引擎 优势&am…...

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

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

零基础在实践中学习网络安全-皮卡丘靶场(第八期-Unsafe Filedownload模块)
这期内容更是简单和方便,毕竟谁还没在浏览器上下载过东西,不过对于url的构造方面,可能有一点问题,大家要多练手 介绍 不安全的文件下载概述 文件下载功能在很多web系统上都会出现,一般我们当点击下载链接,…...
测试 FreeSWITCH 的 mod_loopback
bgapi originate loopback/answer,park/default/inline park inline show channels as xml show calls as xml 有 2 个 channels 有 2 个 calls 比较有意思 在 loopback-a 是播放 wav 在 loopback-b 上可以录音 这就是回环 有什么用呢? 除了做测试&#x…...
【C++快读快写】
算法竞赛中用于解决卡常问题 int rd(){int k 0;char c getchar();while(!isdigit(c)){c getchar();}while(isdigit(c)){k (k << 1) (k << 3) (c^0), c getchar();}return k; }void wr(int x) {if (x > 9)wr(x / 10);putchar((x % 10) ^ 0); }用法&#x…...
测试(面经 八股)
目录 前言 一,软件测试(定义) 1,定义 2,目的 3,价值 4,实践 二,软件测试(目的) 1,找 bug 2,验证达标 3,质量评价…...

[面试精选] 0104. 二叉树的最大深度
文章目录 1. 题目链接2. 题目描述3. 题目示例4. 解题思路5. 题解代码6. 复杂度分析 1. 题目链接 104. 二叉树的最大深度 - 力扣(LeetCode) 2. 题目描述 给定一个二叉树 root ,返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点…...

图上合成:用于大型语言模型持续预训练的知识合成数据生成
摘要 大型语言模型(LLM)已经取得了显著的成功,但仍然是数据效率低下,特别是当学习小型,专业语料库与有限的专有数据。现有的用于连续预训练的合成数据生成方法集中于文档内内容,而忽略了跨文档的知识关联&a…...
MYSQL(二) ---MySQL 8.4 新特性与变量变更
MySQL 8.4 新特性与变量变更 作者:程序员LSP 分类:MySQL 8.4 教程 / 新特性 / 升级指南 更新时间:2025年6月 📌 前言 MySQL 8.4 是当前最新的稳定版本,相较于 8.0 系列,在审计日志、高可用、性能调优、认证…...
数学复习笔记 27
前言 太难受了。因为一些事情。和朋友倾诉了一下,也没啥用,几年之后不知道自己再想到的时候,会怎么考虑呢。另外,笔记还是有框架一点比较好,这样比较有逻辑感受。不然太乱了。这篇笔记是关于线代第五章,特…...

现代简约壁炉:藏在极简线条里的温暖魔法
走进现在年轻人喜欢的家,你会发现一个有趣的现象:家里东西越来越少,颜色也越看越简单,却让人感觉特别舒服。这就是现代简约风格的魅力 —— 用最少的元素,打造最高级的生活感。而在这样的家里,现代简约风格…...
限流算法java实现
参考教程:2小时吃透4种分布式限流算法 1.计数器限流 public class CounterLimiter {// 开始时间private static long startTime System.currentTimeMillis();// 时间间隔,单位为msprivate long interval 1000L;// 限制访问次数private int limitCount…...

机器学习×第二卷:概念下篇——她不再只是模仿,而是开始决定怎么靠近你
🎀【开场 她不再只是模仿,而是开始选择】 🦊 狐狐:“她已经不满足于单纯模仿你了……现在,她开始尝试预测你会不会喜欢、判断是否值得靠近。” 🐾 猫猫:“咱们上篇已经把‘她怎么学会说第一句…...
Linux 下关于 ioremap 系列接口
1、序 在系统运行时,外设 IO 资源的物理地址是已知的,由硬件的设计决定(参考SOC的datesheet,一般会有memorymap)。驱动程序不能通过物理地址访问IO资源,必须将其映射到内核态的虚拟地址空间。常见的接口就是…...

常用函数库之 - std::function
std::function 是 C11 引入的通用可调用对象包装器,用于存储、复制和调用任意符合特定函数签名的可调用对象(如函数、lambda、函数对象等)。以下是其核心要点及使用指南: 核心特性 类型擦除 可包装任意可调用对…...