嵌入式系统与嵌入式 C 语言(1)
目录
1. 嵌入式系统的核心特征
嵌入式系统是 “嵌入到对象体系中的专用计算机系统”,与通用计算机相比具有显著差异:
- 资源受限:CPU 性能较弱(8 位 / 16 位 MCU 为主流)、内存容量小(KB 级 RAM/ROM)、存储有限(多无硬盘,依赖 Flash)
- 硬件相关性强:直接操作硬件寄存器,程序需适配特定芯片(如 STM32、51 单片机、ESP32)
- 实时性要求:多数需响应外部事件(如传感器信号、按键触发),部分场景需硬实时(如工业控制)
- 低功耗需求:多采用电池供电(如物联网节点),需优化功耗延长续航
- 稳定性优先:多为长期运行系统(如汽车 ECU),需避免崩溃与内存泄漏
1.1 嵌入式 C 语言的特殊性
嵌入式 C 语言以标准 C 为基础,扩展了硬件操作能力,同时存在严格约束:
| 特性维度 | 标准 C 语言 | 嵌入式 C 语言 |
| 编程目标 | 逻辑实现、跨平台 | 硬件控制、资源优化、实时响应 |
| 操作对象 | 软件数据结构 | 寄存器、IO 口、外设(UART/SPI) |
| 内存管理 | 依赖 OS 内存分配 | 直接操作物理内存,慎用动态分配 |
| 执行环境 | 通用操作系统(Windows/Linux) | 无 OS 或实时操作系统(RTOS) |
| 调试方式 | 软件调试器(GDB) | 硬件仿真器(J-Link)+ 在线调试 |
1.2 嵌入式 C 的核心应用场景
- 工业控制:PLC(可编程逻辑控制器)、电机驱动、传感器数据采集
- 物联网终端:智能门锁、环境监测节点、无线传感器网络
- 消费电子:家电控制器、智能穿戴设备(手环、手表)
- 汽车电子:车载 ECU(发动机控制单元)、车身控制器
2. 嵌入式 C 开发环境搭建
2.1 核心开发工具链
嵌入式开发需 “编译器 + IDE + 仿真器” 三位一体的工具链,以主流 ARM Cortex-M 系列 MCU 为例:
1. 交叉编译器
- ARM GCC:开源交叉编译器,支持 ARM 架构 MCU,编译输出二进制文件(.bin/.hex)
- Keil MDK:ARM 官方 IDE 集成编译器,支持 STM32 等主流 MCU,自带器件库
- IAR Embedded Workbench:高优化编译器,适合对代码体积 / 速度要求高的场景
2.集成开发环境(IDE)
| IDE 名称 | 适配架构 | 核心优势 | 适用场景 |
| Keil MDK | ARM Cortex-M | 器件库丰富,调试直观 | STM32 开发、入门学习 |
| VS Code + PlatformIO | 跨架构(ARM/51/RISC-V) | 插件生态强,支持多平台 | 多芯片项目、自定义开发 |
| MPLAB X | PIC 系列 MCU | 微芯官方支持,调试稳定 | PIC 单片机开发 |
3. 硬件调试工具
- 仿真器:J-Link(支持 ARM)、ST-Link(STM32 专用),实现在线调试、程序下载
- 开发板:STM32F103C8T6(最小系统板)、51 单片机开发板,用于硬件验证
- 辅助工具:示波器(测信号波形)、逻辑分析仪(抓总线数据)、万用表(测电压 / 电流)
3. C语言的关键字
- C 语言标准共定义 32 个关键字.特别注意:关键字不能作为标识符(变量名、函数名等)使用
| 关键字 | 作用说明 |
| auto | 声明自动变量 |
| break | 跳出当前循环或 switch 语句 |
| case | 用于 switch 语句中,指定分支条件 |
| char | 声明字符型变量或函数返回值类型 |
| const | 声明只读变量(常量),其值不能被修改 |
| continue | 结束本次循环,直接进入下一次循环 |
| default | 用于 switch 语句中,指定默认分支(当所有 case 不匹配时执行) |
| do | 与 while 配合使用,构成 do-while 循环结构 |
| double | 声明双精度浮点型变量或函数返回值类型 |
| else | 与 if 配合使用,指定 if 条件不成立时执行的分支 |
| enum | 声明枚举类型 |
| extern | 声明外部变量或函数(表示该变量 / 函数在其他文件中定义) |
| float | 声明单精度浮点型变量或函数返回值类型 |
| for | 用于构成 for 循环结构 |
| goto | 无条件跳转语句,跳转到指定标签位置(不推荐频繁使用) |
| if | 条件判断语句,指定条件成立时执行的分支 |
| int | 声明整型变量或函数返回值类型 |
| long | 用于声明长整型(如 long int)或长双精度型(long double) |
| register | 声明寄存器变量(建议编译器将变量存储在寄存器中,以提高访问速度) |
| return | 用于函数中,返回函数值并结束函数执行 |
| short | 修饰符,用于声明短整型(如 short int) |
| signed | 声明有符号类型(可表示正数、负数和 0) |
| sizeof | 计算变量或数据类型所占的字节数 |
| static | static静态变量声明符 |
| struct | 声明结构体类型 |
| switch | 多分支条件语句,根据表达式的值跳转到对应的 case 分支 |
| typedef | 为已有数据类型定义新的别名 |
| union | 声明共用体(联合体)类型,所有成员共享同一块内存空间 |
| unsigned | 修饰符,声明无符号类型(仅表示非负数) |
| void | 1. 声明函数无返回值;2. 声明无类型指针;3. 表示空参数列表 |
| volatile | 提示编译器变量的值可能被意外修改,防止编译器过度优化(每次都从内存读取) |
| while | 用于构成 while 循环或 do-while 循环结构 |
4. C语言的运算符
C 语言运算符是用于执行算术运算、逻辑判断、位操作等操作的符号,是连接数据与关键字的 “语法桥梁”。
C 语言运算符按功能可分为 8 大类
| 类别 | 运算符 | 功能说明 | 示例 |
| 算数运算符 | + | 加法 | 5 + 3 |
| - | 减法 | 5 - 3 | |
| * | 乘法 | 5 * 3 | |
| / | 除法(整数除法取商) | 5 / 3 | |
| % | 取余(仅用于整数) | 5 % 3 | |
| ++ | 自增(前 ++:先增后用;后 ++:先用后增) | int a=3; b=++a; | |
| -- | 自减(前 --:先减后用;后 --:先用后减) | int a=3; b=a--; | |
| 赋值运算符 | = | 简单赋值 | int a = 5; |
| += | 加后赋值(a += b 等价于 a = a + b) | a=5; a += 3; | |
| -= | 减后赋值 | a=5; a -= 3; | |
| *= | 乘后赋值 | a=5; a *= 3; | |
| /= | 除后赋值 | a=5; a /= 3; | |
| %= | 取余后赋值 | a=5; a %= 3; | |
| 关系运算符 | == | 等于 | 5 == 3 |
| != | 不等于 | 5 != 3 | |
| > | 大于 | 5 > 3 | |
| < | 小于 | 5 < 3 | |
| >= | 大于等于 | 5 >= 5 | |
| <= | 小于等于 | 5 <= 3 | |
| 逻辑运算符 | && | 逻辑与(短路求值:两边都为真才为真) | (5>3) && (2>1) |
| || | 逻辑或(短路求值:至少一边为真则为真) | (5>3) || (2<1) | |
| ! | 逻辑非(取反) | !(5>3) | |
| 位运算符 | & | 按位与(对应位都为 1 则为 1) | 5 & 3(二进制 101 & 011) |
| | | 按位或(对应位有 1 则为 1) | 5 | 3(101 | 011) | |
| ^ | 按位异或(对应位不同则为 1) | 5 ^ 3(101 ^ 011) | |
| ~ | 按位取反(0 变 1,1 变 0) | ~5(~000...0101) | |
| << | 左移(各二进制位左移,右边补 0) | 5 << 1(101 << 1) | |
| >> | 右移(各二进制位右移,左边补符号位) | 5 >> 1(101 >> 1) | |
| 条件运算符 | ?: | 三目运算符(条件?表达式 1: 表达式 2,条件为真取表达式 1,否则取表达式 2) | (5>3) ? 10 : 20 |
| 指针运算符 | & | 取地址(获取变量的内存地址) | &a |
| * | 指针解引用(获取指针指向的变量值) | *p | |
| 其他运算符 | sizeof | 计算变量或类型的字节数 | sizeof(int) |
| , | 逗号运算符(从左到右执行,返回最后一个表达式结果) | a=2, b=3, a+b |
5. C语言的数据类型
| 类型类别 | 具体类型 | 说明 | 示例声明 |
| 基本类型 | char | 字符型,通常占 1 字节,可表示 ASCII 字符或小整数 | char c = 'A'; |
| short | 短整型,通常占 2 字节,范围约为 - 32768~32767 | short num = 100; | |
| int | 整型,通常占 4 字节,范围约为 - 2147483648~2147483647 | int age = 25; | |
| long | 长整型,通常占 4 或 8 字节,范围比 int 更大 | long population = 1000000L; | |
| long long | 双长整型,通常占 8 字节,范围约为 - 9e18~9e18 | long long big = 123456789LL; | |
| float | 单精度浮点型,占 4 字节,精度约 6~7 位有效数字 | float pi = 3.14f; | |
| double | 双精度浮点型,占 8 字节,精度约 15~17 位有效数字 | double price = 99.99; | |
| long double | 长双精度浮点型,占 8 或 16 字节,精度更高 | long double weight = 123.45L; | |
| 类型修饰符 | signed | 有符号类型(默认),可表示正数、负数和 0 | signed int a = -5; |
| unsigned | 无符号类型,仅表示非负数,范围更大 | unsigned int b = 100; | |
| 构造类型 | 数组 | 相同类型元素的集合,连续存储 | int arr[5] = {1,2,3,4,5}; |
| struct | 结构体,可包含不同类型的成员,自定义复合类型 | struct Student { char name[20]; int age; }; | |
| union | 共用体,所有成员共享同一块内存空间,大小为最大成员的大小 | union Data { int i; float f; }; | |
| enum | 枚举类型,定义命名的整数常量集合,提高代码可读性 | enum Color { RED, GREEN, BLUE }; | |
| 指针类型 | 指针 | 存储变量的内存地址,可指向任意数据类型 | int *p; char *str; |
| 空类型 | void | 表示无类型,用于声明无返回值的函数、无类型指针或空参数列表 |
6. 变量
C 语言中的变量是程序运行过程中用于存储和操作数据的基本单元,其核心作用是临时或持久化保存程序执行过程中产生的数据(如数值、字符、地址等),并通过变量名实现对这些数据的便捷访问和修改。变量根据类型(如整型、浮点型、字符型等)确定可存储的数据范围和操作方式,根据作用域(全局或局部)决定其可被访问的范围,使得程序能够动态处理不同数据、记录中间结果(如计算过程中的临时值)、接收用户输入或输出结果,从而实现复杂的逻辑运算和数据处理。简单来说,变量是程序与数据之间的 “桥梁”,没有变量,程序将无法灵活地存储和操作数据,也就无法实现动态的功能逻辑。
| 变量类型 | 声明格式 | 示例说明 | 示例代码 |
| 基本类型变量 | 类型名 变量名; | 声明一个 int 类型变量 age(未初始化);声明 int 类型变量 score 并初始化为 90 | int age;int score = 90; |
| 类型名 变量 1, 变量 2, ...; | 声明 3 个 float 类型变量 x(未初始化)、y(初始化为 3.14)、z(未初始化) | float x, y = 3.14f, z; | |
| signed/unsigned + 类型名 | 声明有符号 char 变量 c(值为 - 5);声明无符号 int 变量 count(值为 100,只能表示非负数) | signed char c = -5;unsigned int count = 100; | |
| 数组变量 | 类型名 数组名 [长度]; | 声明可存储 5 个 int 的数组 nums(未初始化);声明 3 个 int 的数组 arr,初始值为 1、2、3 | int nums[5];int arr[3] = {1, 2, 3}; |
| 类型名 数组名 [] = {元素}; | 声明字符数组 str,未指定长度,由 "hello" 自动确定长度为 6(含结束符 '\0') | char str[] = "hello"; | |
| 指针变量 | 类型名 * 指针名; | 声明 int 变量 a(值为 5);声明 int 指针 p,指向 a 的地址(p 存储 a 的内存位置) | int a = 5;int *p = &a; |
| void * 通用指针名; | 声明通用指针 ptr,可指向任意类型(如后续可指向 int、char 等,需强制转换) | void *ptr; | |
| 结构体变量 | struct 结构体名 变量名; | 先定义 Student 结构体(含 name 和 age 成员),再声明该类型变量 stu | struct Student {char name[20];int age;};struct Student stu; |
| 结构体定义时直接声明变量 | 定义 Point 结构体(含 x、y 坐标),同时声明变量 p1 和 p2,p2 初始化为 (3,4) | struct Point {int x;int y;} p1, p2 = {3, 4}; | |
| 枚举变量 | enum 枚举名 变量名; | 定义 Week 枚举(含周一至周三),声明变量 today 并赋值为周二(TUE 对应数值 1) | enum Week { MON, TUE, WED };enum Week today = TUE; |
| 枚举定义时直接声明变量 | 定义 Color 枚举(红、绿),同时声明变量 c 并赋值为绿色(GREEN 对应数值 1) | enum Color { RED, GREEN } c = GREEN; | |
| 共用体变量 | union 共用体名 变量名; | 定义 Data 共用体(含 int 和 float 成员,共享内存),声明该类型变量 d | union Data {int i;float f;};union Data d; |
变量名称需遵循 C 语言标识符规则,嵌入式开发中需额外注重 “语义化、模块化”,提升代码可读性:
规则:由字母、数字、下划线组成,首字符不能为数字,不能与关键字重名;
规范:采用 “前缀 + 语义” 命名法,前缀标识模块 / 功能,语义说明变量用途:
禁忌:避免a、b等无意义名称,禁止与关键字重名(如int while = 0;编译报错)。
变量分全局变量和局部变量
| 特性 | 全局变量(GlobalVariable) | 局部变量(LocalVariable) |
| 定义位置 | 定义在所有函数外部(包括main函数之外) | 定义在函数内部、函数的参数列表中,或复合语句(如if、for的{}块)内部 |
| 作用域 | 从定义位置开始,在整个程序文件中可见,其他文件可通过extern声明访问 | 仅在定义它的函数或复合语句内部可见,出了该范围则无法访问 |
| 生命周期 | 从程序启动时创建,直到程序结束时销毁 | 从进入定义它的函数 / 复合语句时创建,函数返回 / 复合语句执行完毕时销毁 |
| 默认值 | 未初始化时,默认值为 0(数值类型)或空字符(字符类型) | 未初始化时,值为随机的垃圾值(不确定) |
| 存贮区域 | 存储在静态存储区(全局数据区) | 存储在栈区(函数调用时分配, |
注意事项:
1、当全局变量与局部变量同名时,在局部变量的作用域内,局部变量会覆盖全局变量(优先访问局部变量)
2、全局变量会一直占用内存,过多使用可能导致内存浪费,且会降低代码的封装性和安全性(任何函数都可能修改它
3、局部变量仅在函数执行时存在,内存使用更高效,是程序中更常用的变量类型
4、函数的参数也属于局部变量,作用域和生命周期与函数内部定义的局部变量相同
7. 常量
常量是程序运行过程中值不能改变的量,主要有两种定义方式:
- 宏定义(#define)
#define PI 3.14159 // 定义宏常量PI
#define MAX(a, b) (a > b ? a : b) // 定义宏函数(带参数的宏)
int main() {
float area = PI * 2 * 2; // 使用宏常量
int max_num = MAX(5, 8); // 使用宏函数,结果为8
printf("面积:%.2f\n", area);
printf("最大值:%d\n", max_num);
return 0;
}
注意:宏定义在预处理阶段进行文本替换,无类型检查,且末尾不要加逗号。
2. const 关键字
const int MAX_SIZE = 100; // 定义const常量MAX_SIZE
int main() {
MAX_SIZE = 200; // 错误,const常量的值不能修改
return 0;
}
对比宏定义与 const:const 常量有类型检查,更安全;宏定义可定义带参数的宏,灵活性更高。
8. 数组
C 语言中的数组是一组相同数据类型元素的集合,其核心作用是高效批量存储和管理同类型数据。它通过连续的内存空间存储元素,允许通过下标快速访问任意元素,结合循环可便捷实现批量处理(如遍历、统计、修改等);作为函数参数时能简化批量数据传递,避免多个独立变量的繁琐操作;同时,字符数组是 C 语言中存储和处理字符串的基础,也是构建矩阵等复杂数据结构的基石。数组的连续存储特性还能优化内存使用效率,提升程序运行性能,但需注意其长度固定(静态数组)和下标不可越界的特性。
在 C 语言中,数组是一组相同数据类型元素的集合,元素在内存中连续存储。以下是数组的定义方式、分类及示例
| 数组类型 | 定义格式 | 说明 | 示例代码 |
| 一维数组 | 数据类型 数组名 [元素个数]; | 最基本的数组形式,元素按线性排列 | int scores [5]; // 定义可存储 5 个 int 的数组float weights [3] = {50.5, 60.2, 70.1}; // 定义并初始化 |
| 数据类型 数组名 [] = {元素 1, 元素 2, ...}; | 不指定长度,由初始化元素个数自动确定数组长度 | char fruits [] = {"apple", "banana"}; // 长度为 2 的字符串数组 | |
| 二维数组 | 数据类型 数组名 [行数][列数]; | 可理解为 "数组的数组",按行优先存储 | int matrix [2][3]; // 2 行 3 列的二维 int 数组int table [2][2] = {{1,2}, {3,4}}; // 初始化 |
| 数据类型 数组名 [行数][] = {初始化列表}; | 列数必须指定,行数可省略(由初始化列表确定) | int arr [][3] = {{1,2,3}, {4,5,6}}; // 自动确定为 2 行 3 列 | |
| 字符数组 | char 数组名 [长度]; | 用于存储字符序列,可表示字符串(需以 '\0' 结尾) | char name [10] = "Alice"; // 字符串初始化,自动添加 '\0'char str [5] = {'H','i','\0'}; // 显式添加结束符 |
| 动态数组 | 数据类型 * 指针名;指针名 = malloc (长度 * sizeof (数据类型)); | 运行时动态分配内存,长度可动态指定(需包含 stdlib.h) | int nums;nums = (int) malloc (5 * sizeof (int)); // 动态分配 5 个 int 的空间 |
特别注意:
数组名:代表数组首元素的地址,是一个常量指针,不能被赋值(如arr = &x;是错误的)。
元素访问:通过下标[ ]访问,下标从 0 开始(如scores[0]是数组的第一个元素)。
初始化规则:
初始化元素少于数组长度时,剩余元素自动初始化为 0(全局数组)或随机值(局部数组)。
字符数组若用字符串常量初始化(如"hello"),会自动在末尾添加空字符'\0',计算长度时需预留该位置。
动态数组注意事项:
使用malloc分配的内存需用free(指针名)释放,避免内存泄漏。
动态数组长度可通过变量指定(如int n=5; int *arr = malloc(n*sizeof(int));),而静态数组长度必须是常量。
更多推荐



所有评论(0)