当前位置: 首页 > news >正文

嵌入式与C/C++基础详解:从小白到高手

面向小白的嵌入式与 C/C++ 基础详解(由浅入深)



目录(快速导航)

  • 嵌入式通信与信号基础
  • C/C++ 常用关键字详解(volatile, static, const
  • 指针专题(分类、区别、示例)
  • 常见排序算法(选择、快速、归并、堆、基数)
  • 避免常见坑(野指针、内存泄漏、ISR 中的注意事项)
  • 面试高频点与练习路线
  • 总结与学习建议

嵌入式通信与信号基础(从物理层到协议层)

差分信号 vs 单端信号(简单比喻)

  • 单端信号:把一根数据线对地参考,例如很多常见的 GPIO / UART TX。优点:线路简单;缺点:抗干扰差,线长受限。
  • 差分信号:用两根线传同一信号的互补(例如 A、B),接收端比较两线的电压差(如 CAN、RS-485、USB)。优点:抗共模干扰强,适合长距离和噪声环境。

类比:单端像“喊话给一个人”,差分像“两个人一左一右,各自喊相反的话,听的人只听两者差别”,更稳健。

开放-漏极(Open-Drain/Open-Collector)与上拉/下拉

  • 开放-漏极输出本身只能“拉低”(驱动为 0),要把线拉高需要外部上拉电阻。I²C 就用开放-漏极(开漏)+ 上拉来实现多主机共享总线与仲裁。
  • 上拉电阻:把默认电平拉到高电平,当总线上有器件把线拉低时总线为低电平。

仲裁(Arbitration)与标准/扩展帧

  • 在多主机总线(如 CAN)上,如果多个节点同时发送,仲裁机制决定哪个帧能在总线上继续发送(通常先发送“更优先”的标识位)。CAN 有**标准帧(11-bit ID)扩展帧(29-bit ID)**两种格式(即“标准”和“扩展”概念)。I²C 的仲裁则基于 SDA/SCL 线上的读/写比对。
  • 这些概念对理解嵌入式现场总线(如 CAN)与其设计非常重要。

C/C++ 常用关键字详解(针对嵌入式与多线程/中断场景)

volatile(关键字用途与示例)

作用:告诉编译器“该变量可能在程序外部被改动(例如中断、DMA、外设映射内存、其他线程)”,编译器不能对它做优化(不能缓存寄存器、不能删掉看似多余的访问)。

典型场景:中断服务例程(ISR)中修改的标志位,或者映射到外设寄存器的地址。

示例(C++ 风格,注释逐行解释):

volatile bool data_ready = false; // 可能在中断中被设置,编译器不得优化掉对它的检查void ISR_handler() {// 中断触发时设置标志data_ready = true; // 写入 volatile 变量,确保实际写回内存/寄存器
}int main() {while (!data_ready) {// 如果没有 volatile,编译器可能把 data_ready 缓存在寄存器里导致死循环// 有了 volatile,每次都从内存读取,能及时感知 ISR 的修改}// 继续处理
}

注意volatile 不是互斥或原子性保障(并发需用原子/锁机制)。它只是阻止编译器做假优化。


static(局部静态、全局静态、作用域和生命周期)

  • 局部 static 变量:函数内定义但生命周期贯穿程序运行(只初始化一次)。适用于需要函数记忆某个值但不想暴露为全局的场景。
  • 文件级 static(在 C/C++ 中):修饰全局变量或函数,限制其链接范围为当前源文件(即隐藏符号)。

示例(局部 static):

int counter() {static int cnt = 0; // 只初始化一次,保留上次调用的值cnt++;return cnt;
}

const(只读语义)与 volatile 的组合

  • const 表示变量不可被修改(语义),常用于只读数据或接口约束。
  • volatile const 可表示“硬件寄存器的只读位”(比如某些状态寄存器),既不允许程序改写,也阻止编译器缓存读取结果。

指针专题(分类、区别、示例)——这是裸 C/C++ 的核心(详细且举例)

基本概念回顾

  • 指针:保存另一个变量地址的变量。
  • 指针数组(array of pointers):数组里的元素都是指针。
  • 数组指针(pointer to array):指向整个数组的指针(类型是“指向数组”的指针)。

示例对比(并配逐行注释):

int a = 10, b = 20;
int* p1 = &a;           // 指针,指向 int
int* arr_of_ptr[2];     // 指针数组:包含两个 int* 元素
arr_of_ptr[0] = &a;
arr_of_ptr[1] = &b;// 数组指针:指向一个含 2 个 int 的数组
int arr[2] = {1, 2};
int (*p_arr)[2] = &arr; // p_arr 的类型是 "指向含 2 个 int 的数组的指针"

记忆小技巧

  • “指针数组”是“数组(元素类型为指针)”。
  • “数组指针”是“指针(指向数组)”。

函数指针 vs 指针函数(不要混淆)

  • 函数指针(pointer to function):指向函数,可以通过它调用函数。
  • 指针函数(function returning pointer):函数的返回类型是指针。

示例(函数指针):

int add(int x, int y) { return x + y; }// 定义函数指针:指向返回 int、接收两个 int 参数的函数
int (*fp)(int, int) = &add; // 或 = add;int result = fp(2, 3); // 通过函数指针调用

示例(指针函数):

int* makePointer() {static int value = 42; // 使用 static 避免返回指向局部变量的野指针return &value;         // 返回指针(指向静态变量)
}

常见指针相关错误与如何避免(面向初学者)

  1. 野指针(Dangling Pointer):指针指向的内存已被释放或局部变量已离开作用域。

    • 解决办法:释放后立即 p = nullptr;;不要返回指向局部栈变量的指针(除非它是 static)。
  2. 内存泄漏:分配但不释放内存。

    • 解决办法:成对使用 new/delete 或优先用智能指针(C++11+ 的 std::unique_ptr, std::shared_ptr)。
  3. 未初始化指针:未赋初值就使用。

    • 解决办法:定义时就初始化为 nullptr

示例(避免野指针):

int* p = new int(5);
delete p;
p = nullptr; // 防止后续误用成为野指针

常见排序算法(由浅入深)——理解核心思想比记代码更重要

每个算法说明:原理、时间复杂度、空间复杂度、稳定性 + 一个简洁的示例实现(带注释)

选择排序(Selection Sort)——最容易理解的入门算法

  • 原理:每次从未排序区选最小(或最大)元素放到已排序区末尾。
  • 时间复杂度O(n2)O(n^2)O(n2),空间复杂度 O(1)O(1)O(1),不稳定。

示例(C++):

// 选择排序:把数组 a 从小到大排序
void selection_sort(int a[], int n) {for (int i = 0; i < n - 1; ++i) {int min_idx = i; // 记录当前未排序区的最小元素下标for (int j = i + 1; j < n; ++j) {if (a[j] < a[min_idx]) min_idx = j;}// 如果最小元素不是当前 i,则交换if (min_idx != i) std::swap(a[i], a[min_idx]);}
}

快速排序(Quicksort)——常用且高效(平均性能好)

  • 原理:分治。选一个 pivot,把数组划分为小于 pivot 和大于 pivot 两部分,然后递归排序两边。
  • 平均时间复杂度O(nlog⁡n)O(n\log n)O(nlogn),最坏 O(n2)O(n^2)O(n2)(不好的 pivot 选择),空间复杂度平均 O(log⁡n)O(\log n)O(logn)(递归栈),不稳定。

示例(简洁版,递归):

int partition(int a[], int l, int r) {int pivot = a[r]; // 以最右元素为基准(简单实现)int i = l - 1;for (int j = l; j < r; ++j) {if (a[j] <= pivot) {++i;std::swap(a[i], a[j]);}}std::swap(a[i + 1], a[r]);return i + 1;
}void quicksort(int a[], int l, int r) {if (l < r) {int p = partition(a, l, r);quicksort(a, l, p - 1);quicksort(a, p + 1, r);}
}

实践建议:实际工程中常用随机化 pivot 或“三数取中”避免退化。

归并排序(Merge Sort)——稳定且易并行化

  • 原理:分治,递归将数组分为两半,分别排序后合并。
  • 时间复杂度O(nlog⁡n)O(n\log n)O(nlogn),空间复杂度 O(n)O(n)O(n)(需要临时数组),稳定。

堆排序(Heap Sort)——不稳定但空间 O(1)O(1)O(1)O(nlog⁡n)O(n\log n)O(nlogn) 算法

  • 原理:构建最大堆(或最小堆),将堆顶移到末尾,缩堆,重复。
  • 复杂度O(nlog⁡n)O(n\log n)O(nlogn),空间 O(1)O(1)O(1),不稳定。

基数排序(Radix Sort)——非比较排序(适合整数/固定长度)

  • 原理:按位(或按字节)分配计数并收集,逐位排序(通常 LSD 或 MSD)。
  • 复杂度:若位数为 kkk,时间约 O(k⋅n)O(k \cdot n)O(kn);在某些场景比比较排序更快。

中断(ISR)与并发注意事项(面向嵌入式初学者)

ISR 中常见规则(简短清单)

  • 尽量短:中断处理尽量只做最必要的工作(设置标志、读取/清除外设中断位),复杂处理交给主循环或任务。
  • 使用 volatile:ISR 修改的共享变量要声明为 volatile
  • 保护共享资源:若 ISR 与主程序共享变量并且存在多字节访问,主程序访问时需禁中断或使用原子操作以避免竞态。
  • 不在 ISR 中使用可能阻塞的操作(比如 malloc、printf(某些平台)或长时间 I/O)。

示例(ISR 设置标志,主循环处理):

volatile bool flag = false;void ISR() {flag = true; // 快速设置标志,返回
}int main() {while (1) {if (flag) {flag = false; // 处理前先清标志// 在这里执行耗时处理(非中断上下文)}}
}

http://www.dtcms.com/a/447614.html

相关文章:

  • 网站制作找哪家公司好织梦cms做网站教程视频
  • 响应式网站建设的好处硬件开发设计流程
  • 佛山响应式网站网站粘度
  • 随州网站建设学校wordpress搜索框制作教程
  • 系数调整:四项平方和的最小值攻略
  • 网站内容的创新公众号小程序怎么开通
  • 网站如何更新内容如何注册商标品牌
  • 宁夏网站建设联系电话中国最新军事新闻最新消息视频
  • 没有网站怎么做cps旅游网站建设网
  • 同性男做的视频网站设计制作一个 个人主页网站
  • 专业网站有哪些平台dz增加网站标签
  • 大模型训练微调和推理阶段的显存对比分析
  • 高端的家居行业网站开发昆明网站推广价格
  • 做商城网站技术要点成都网站建设方案服务
  • 企业网站系统建设需求调研表给平面设计素材网站做素材挣钱吗
  • ps免费模板网站视频号关键词搜索排名
  • 网络营销企业网站优化Wordpress 新建标签
  • 网站建设层级图千图网素材解析网站开发
  • 辽阳网站推广公众号运营收费标准
  • C#实现三菱FX3SA PLC串口通信测试实例
  • 公司网站如何注册政务门户网站建设
  • 10.5交作业
  • 网站建设的数据所有权网页浏览器是系统软件吗
  • 事业单位网站后台建设方案建立网站一般包括什么等方式
  • 外贸做网站公司哪家好郑州高端网站案例
  • html5做网站优势东莞公司企业设计网站建设
  • 制作网站图片不显示百度招商加盟
  • 建设实验教学网站的作用有免费建站的网站
  • 表白网页制作免费网站深圳人口1756万
  • 沈阳网站建设找思路手机上干点啥能挣零花钱