
目录
[1. 解释 C 语言中的 static 关键字的作用](#1. 解释 C 语言中的 static 关键字的作用)
[2. const 关键字在 C 语言中如何使用](#2. const 关键字在 C 语言中如何使用)
[3. 解释 volatile 关键字的重要性](#3. 解释 volatile 关键字的重要性)
[4. 什么是指针?并举例说明其用法](#4. 什么是指针?并举例说明其用法)
[5. 解释结构体(struct)在 C 语言中的使用](#5. 解释结构体(struct)在 C 语言中的使用)
[6. 枚举(enum)类型在 C 语言中的作用](#6. 枚举(enum)类型在 C 语言中的作用)
[7. 解释 C 语言中的联合(union)](#7. 解释 C 语言中的联合(union))
1. 解释 C 语言中的 static 关键字的作用
static 关键字在C语言中核心作用有3类,在嵌入式开发中应用广泛,核心是控制生命周期和作用域:
- 修饰局部变量:变量存储在静态存储区,生命周期与程序一致,仅初始化1次,函数调用结束后值不丢失。嵌入式场景中常用于统计中断触发次数、保存设备运行状态等,例如:
cpp
void exti_isr(void)
{
static uint32_t interrupt_count = 0; // 仅初始化1次,持续保存计数
interrupt_count++; // 每次中断触发,计数递增
}
- 修饰全局变量:限制该变量的作用域仅为当前编译单元(.c文件),避免多文件命名冲突,是嵌入式模块化开发的基础,防止全局变量被其他文件误修改。
- 修饰函数:限制函数的作用域仅为当前编译单元,仅能在本文件中被调用,避免函数名冲突,同时隐藏内部实现细节,提升代码安全性和可维护性。
2. const 关键字在 C 语言中如何使用
const 用于定义"只读"元素,核心作用是保护数据不被误修改,在嵌入式开发中常用于硬件寄存器、配置参数等场景,具体用法分为4类:
- 修饰普通变量:定义只读变量,初始化后不可修改,例如 const int MAX_BUFFER_SIZE = 128;,常用于定义设备配置参数(如缓冲区大小、波特率)。
- 修饰指针(核心用法,嵌入式高频):
cpp
#define GPIOA_BASE 0x40010800
const uint32_t *const GPIOA_CRL = (uint32_t *)(GPIOA_BASE + 0x00); // 只读寄存器,不可修改指针和内容
const int *p:指针指向的内容只读,指针本身可修改(常用于读取硬件寄存器值,防止误写);
int *const p:指针本身只读,指向的内容可修改(常用于固定指向某一硬件寄存器地址);
const int *const p:指针和指向的内容均只读(最严格的保护,常用于核心寄存器)。
- 修饰数组:定义只读数组,例如 const char device_name[] = "STM32F103";,常用于存储设备固定信息(设备名、版本号)。
- 修饰函数参数:防止函数内部修改传入的参数值,例如 void print_info(const char *info),避免误修改传入的字符串或数据。
3. 解释 volatile 关键字的重要性
volatile 核心作用:告诉编译器该变量可能被硬件、中断、其他线程异步修改,禁止编译器对该变量的读写进行优化,确保每次读写都直接访问内存(而非寄存器缓存),是嵌入式开发中避免"优化陷阱"的关键,直接影响系统稳定性。
重要性体现在2个核心场景:
- 硬件相关变量:嵌入式中,硬件寄存器的值可能被外设异步修改(如GPIO输入电平、ADC采样值),若不加volatile,编译器会优化为缓存到寄存器,导致读取到旧值,例如:
cpp
// 错误:未加volatile,编译器可能优化为只读取一次
uint32_t *gpio_input = (uint32_t *)0x40010804;
while(*gpio_input == 0); // 若硬件修改了gpio_input的值,可能无法检测到
// 正确:加volatile,每次读取都访问内存
volatile uint32_t *gpio_input = (volatile uint32_t *)0x40010804;
while(*gpio_input == 0); // 能实时检测硬件电平变化
- 中断相关变量:中断服务程序中修改的全局变量,主程序读取时若不加volatile,编译器会优化为缓存值,导致主程序无法获取中断修改后的值(如中断标志位、计数变量)。
4. 什么是指针?并举例说明其用法
指针:本质是存储内存地址的变量,通过指针可以直接访问内存中的数据,是C语言的核心特性,也是嵌入式开发中操作硬件、优化内存的关键。
核心用法及嵌入式实例:
- 访问硬件寄存器(嵌入式最核心用法):通过指针直接操作寄存器地址,实现对硬件的控制,例如操作STM32的GPIO输出高电平:
cpp
#define GPIOA_ODR 0x4001080C // GPIOA输出数据寄存器地址
volatile uint32_t *gpioa_odr = (volatile uint32_t *)GPIOA_ODR;
*gpioa_odr |= (1 << 0); // 通过指针修改寄存器,PA0输出高电平
- 操作数组:指针可替代数组下标,访问效率更高,例如:
cpp
int data[] = {1,2,3,4,5};
int *p = data; // 指针p指向数组首地址
for(int i=0; i<5; i++)
{
printf("%d ", *(p+i)); // 通过指针访问数组元素,等价于data[i]
}
- 动态内存分配:通过指针接收malloc分配的内存地址,实现灵活的内存管理,例如:
cpp
int *buf = (int *)malloc(10 * sizeof(int)); // 分配10个int类型的内存
if(buf != NULL)
{
buf[0] = 100; // 通过指针操作动态内存
free(buf); // 释放内存,避免泄漏
buf = NULL; // 避免野指针
}
- 函数参数传递:通过指针传递参数,实现"传址调用",让函数修改外部变量的值,例如:
cpp
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
int x=1, y=2;
swap(&x, &y); // 传递变量地址,函数内部修改x和y的值
5. 解释结构体(struct)在 C 语言中的使用
结构体(struct)是C语言中用于自定义复合数据类型的工具,可将多个不同类型的数据(如int、char、指针)封装在一起,形成一个统一的"数据集合",适合描述具有多个属性的对象,在嵌入式开发中应用极广(如设备参数、传感器数据、通信帧)。
核心用法及嵌入式实例:
- 定义结构体类型:
cpp
// 定义传感器数据结构体,包含温度、湿度、时间戳
typedef struct {
float temperature; // 温度(单位:℃)
float humidity; // 湿度(单位:%RH)
uint32_t timestamp;// 时间戳(单位:ms)
} SensorData;
// 定义GPIO配置结构体
typedef struct {
uint8_t pin; // GPIO引脚号
uint8_t mode; // 工作模式(输入/输出)
uint8_t speed; // 输出速度
} GPIO_Config;
- 声明结构体变量并使用:
cpp
// 声明结构体变量
SensorData sensor;
// 赋值
sensor.temperature = 25.5;
sensor.humidity = 60.0;
sensor.timestamp = 1680000000;
// 访问结构体成员
printf("温度:%.1f℃\n", sensor.temperature);
// 结构体指针(嵌入式高频用法,节省内存)
SensorData *p_sensor = &sensor;
printf("湿度:%.1f%%RH\n", p_sensor->humidity); // 指针访问用->
结构体数组:存储多个同类型的复合数据,例如存储多个传感器的采集数据:SensorData sensor_array[5]; // 5个传感器的数据
for(int i=0; i<5; i++){
sensor_array[i].temperature = 25.0 + i;
sensor_array[i].humidity = 60.0 - i;
}
6. 枚举(enum)类型在 C 语言中的作用
枚举(enum)是C语言中用于定义一组有名字的常量(枚举常量)的工具,核心作用是替代魔法数字,让代码更具可读性、可维护性,避免因数字含义不明确导致的错误,在嵌入式开发中常用于表示状态、模式、命令等。
核心用法及嵌入式实例:
- 定义枚举类型:
cpp
// 定义设备运行状态枚举
typedef enum {
DEVICE_IDLE = 0, // 空闲状态
DEVICE_COLLECTING, // 采集状态
DEVICE_COMMUNICATING,// 通信状态
DEVICE_ERROR // 错误状态
} DeviceStatus;
// 定义GPIO模式枚举
typedef enum {
GPIO_INPUT = 0, // 输入模式
GPIO_OUTPUT, // 输出模式(默认递增1,即1)
GPIO_AF, // 复用功能模式(2)
GPIO_ANALOG // 模拟模式(3)
} GPIOMode;
- 使用枚举变量:
cpp
DeviceStatus status = DEVICE_IDLE; // 声明枚举变量并赋值
// 分支判断(可读性远高于if(status == 0))
switch(status){
case DEVICE_IDLE:
printf("设备处于空闲状态\n");
break;
case DEVICE_COLLECTING:
printf("设备正在采集数据\n");
break;
case DEVICE_ERROR:
printf("设备出现错误\n");
break;
}
- 作用总结:枚举常量是编译期常量,不占用运行时内存;可清晰表示一组相关的状态/选项,降低代码维护成本(如修改状态值,只需修改枚举定义,无需修改所有使用处)。
7. 解释 C 语言中的联合(union)
联合(union)又称共用体,是C语言中一种复合数据类型,核心特点是:所有成员共用同一块内存空间,内存大小等于最大成员的大小,同一时间只能有一个成员有效。
核心作用及嵌入式实例:
- 节省内存:当多个数据不同时使用时,用union可减少内存占用,嵌入式系统内存资源有限,此特性尤为重要。
- 数据类型转换:通过union实现同一内存空间的不同数据类型解读(如将4字节int拆分为4个1字节char,或反之),常用于通信数据解析(如串口接收的字节流转换为多字节数据):
cpp
// 定义联合,用于int和char的转换
typedef union {
int32_t data; // 4字节
uint8_t byte[4]; // 4个1字节,与data共用内存
} DataConvert;
// 实例:将int数据拆分为字节流,用于串口发送
DataConvert dc;
dc.data = 0x12345678; // 赋值int类型
// 拆分后,byte[0]=0x78, byte[1]=0x56, byte[2]=0x34, byte[3]=0x12(小端模式)
for(int i=0; i<4; i++){
uart_send_byte(dc.byte[i]); // 逐字节发送
}
- 注意:union的成员共用内存,修改一个成员会覆盖其他成员的值,使用时需确保同一时间只操作一个成员。

