嵌入式系统与嵌入式 C 语言(1)
本文系统介绍了嵌入式C语言开发的核心知识体系,重点包括:1.嵌入式系统特征(资源受限、硬件相关、实时性要求);2.嵌入式C开发工具链(交叉编译器、IDE、仿真器);3.C语言基础语法(32个关键字、8类运算符、数据类型);4.变量与常量的定义与使用规则;5.数组的声明与操作方法。内容全面覆盖嵌入式开发必备的C语言知识点,特别强调其在资源受限环境下的特殊应用场景和优化要求,为嵌入式开发人员提供了清晰
目录
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)