const关键字用于定义只读变量,即该变量的值在初始化后不能被修改。
常量指针 vs 指针常量 vs 指向常量的常量指针
常量指针(Pointer to Constant)
const int *ptr; // 或 int const *ptr;
指针常量(Constant Pointer)
int *const ptr;
指向常量的常量指针(Constant Pointer to Constant)
const int *const ptr;
const在函数参数中的应用
void print_string(const char *str); // 保护字符串不被修改
void copy_data(const int *src, int *dst, int len); // 保护源数据
const与数组
const int arr[5] = {1, 2, 3, 4, 5}; // 数组元素不可修改
const char *str = "Hello"; // 字符串内容不可修改
volatile关键字告诉编译器:该变量可能被程序以外的因素修改,禁止编译器对该变量进行优化。
编译器优化原理
编译器会将频繁访问的变量缓存到寄存器中,而不是每次都从内存读取。这在单线程程序中是安全的,但在以下场景会导致问题:
为什么需要volatile
硬件寄存器映射
#define REG (*(volatile uint32_t*)0x40021000)
中断服务程序
volatile int flag = 0; // 在main中
void ISR() { flag = 1; } // 在中断中修改
多线程共享变量
volatile int shared_data = 0; // 多个线程访问
volatile与const共存
volatile const uint32_t *status_reg; // 只读硬件寄存器
volatile不是原子操作
volatile int counter = 0;
// 以下操作不是原子的
counter++; // 实际是:读取-修改-写入 三个步骤
static关键字有多种用途,主要作用是限制变量或函数的作用域。
局部静态变量
void counter() {
static int count = 0; // 只初始化一次
count++;
printf("Count: %d\n", count);
}
全局静态变量
static int file_var = 10; // 仅在当前文件可见
static函数
static void helper_function() { // 仅在当前文件可见
// ...
}
static在嵌入式中的模块化作用
// module.h
void module_init(void);
int module_read(void);
// module.c
static int internal_state = 0; // 模块内部状态,外部不可访问
static void update_state(void) { /* ... */ }
void module_init(void) {
internal_state = 0;
update_state();
}
int module_read(void) {
return internal_state;
}
| 声明方式 | 含义 | 示例 |
|---|---|---|
const int *ptr |
指向常量的指针 | *ptr = 10; ❌ |
int const *ptr |
指向常量的指针(同上) | *ptr = 10; ❌ |
int *const ptr |
常量指针 | ptr = &x; ❌ |
const int *const ptr |
指向常量的常量指针 | 都不能修改 |
| 场景 | 用法 | 说明 |
|---|---|---|
| 硬件寄存器 | #define REG (*(volatile uint32_t*)0x40021000) |
禁止缓存优化 |
| 中断标志 | volatile int irq_flag; |
中断中修改的变量 |
| 多线程变量 | volatile int shared; |
线程间共享数据 |
| 场景 | 用法 | 作用域 | 生命周期 |
|---|---|---|---|
| 局部静态 | static int x; |
函数内 | 程序期 |
| 全局静态 | static int x; |
文件内 | 程序期 |
| 静态函数 | static void f(); |
文件内 | - |
// const_example.c
#include <stdio.h>
// 函数参数中使用const
void print_array(const int *arr, int size) {
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
// arr[i] = 10; // 编译错误:不能修改const数据
}
int main() {
// 1. 常量指针
int a = 10, b = 20;
const int *ptr1 = &a;
// *ptr1 = 30; // 编译错误:不能通过ptr1修改值
ptr1 = &b; // 正确:可以修改指针本身
printf("ptr1 points to: %d\n", *ptr1);
// 2. 指针常量
int *const ptr2 = &a;
*ptr2 = 30; // 正确:可以通过ptr2修改值
// ptr2 = &b; // 编译错误:不能修改指针本身
printf("ptr2 points to: %d\n", *ptr2);
// 3. 指向常量的常量指针
const int *const ptr3 = &b;
// *ptr3 = 40; // 编译错误:不能修改值
// ptr3 = &a; // 编译错误:不能修改指针
printf("ptr3 points to: %d\n", *ptr3);
// 4. const与数组
const int arr[] = {1, 2, 3, 4, 5};
print_array(arr, 5);
// arr[0] = 10; // 编译错误:不能修改const数组
return 0;
}
编译命令:
gcc -o const_example const_example.c
./const_example
// volatile_example.c
#include <stdio.h>
#include <stdint.h>
// 模拟硬件寄存器
#define STATUS_REG (*(volatile uint32_t*)0x40021000)
#define DATA_REG (*(volatile uint32_t*)0x40021004)
// 模拟的寄存器内存(实际中由硬件映射)
static uint32_t simulated_status = 0;
static uint32_t simulated_data = 0;
// 模拟读取状态寄存器
uint32_t read_status() {
return simulated_status;
}
// 模拟写入数据寄存器
void write_data(uint32_t value) {
simulated_data = value;
}
// 中断标志
volatile int interrupt_flag = 0;
// 模拟中断服务程序
void simulate_isr() {
interrupt_flag = 1;
printf("[ISR] Interrupt occurred, flag set to 1\n");
}
int main() {
// 1. 硬件寄存器示例
printf("=== Hardware Register Example ===\n");
simulated_status = 0x01; // 模拟硬件设置状态
printf("Status register: 0x%08X\n", read_status());
write_data(0xDEADBEEF); // 写入数据
printf("Data register: 0x%08X\n", simulated_data);
// 2. 中断标志示例
printf("\n=== Interrupt Flag Example ===\n");
printf("Initial flag: %d\n", interrupt_flag);
// 模拟主循环
for (int i = 0; i < 3; i++) {
if (!interrupt_flag) {
printf("[Main] Working... (iteration %d)\n", i);
// 模拟第2次迭代时发生中断
if (i == 1) {
simulate_isr();
}
} else {
printf("[Main] Handling interrupt!\n");
interrupt_flag = 0; // 清除标志
printf("[Main] Flag cleared: %d\n", interrupt_flag);
}
}
// 3. volatile与const共存
printf("\n=== Volatile + Const Example ===\n");
volatile const uint32_t *ro_reg = &simulated_status;
simulated_status = 0xFF;
printf("Read-only register: 0x%08X\n", *ro_reg);
// *ro_reg = 0x00; // 编译错误:不能通过指针修改(const)
// 但硬件可以修改(volatile)
return 0;
}
编译命令:
gcc -o volatile_example volatile_example.c
./volatile_example
// static_example.c
#include <stdio.h>
// 文件静态变量
static int file_counter = 0;
// 静态函数(仅文件内可见)
static void increment_file_counter() {
file_counter++;
printf("File counter incremented to: %d\n", file_counter);
}
// 局部静态变量函数
void local_static_demo() {
static int call_count = 0; // 只初始化一次
call_count++;
printf("Function called %d time(s)\n", call_count);
}
// 模块化示例:计数器模块
// counter_module.h 声明
void counter_init(void);
int counter_get_value(void);
void counter_increment(void);
// counter_module.c 实现
static int counter_value = 0; // 模块内部状态
void counter_init(void) {
counter_value = 0;
printf("[Counter Module] Initialized\n");
}
int counter_get_value(void) {
return counter_value;
}
void counter_increment(void) {
counter_value++;
printf("[Counter Module] Value: %d\n", counter_value);
}
int main() {
// 1. 文件静态变量
printf("=== File Static Variable ===\n");
increment_file_counter();
increment_file_counter();
increment_file_counter();
// file_counter++; // 如果在其他文件中,将无法访问
// 2. 局部静态变量
printf("\n=== Local Static Variable ===\n");
for (int i = 0; i < 3; i++) {
local_static_demo();
}
// 3. 模块化示例
printf("\n=== Module Pattern ===\n");
counter_init();
for (int i = 0; i < 5; i++) {
counter_increment();
}
printf("[Main] Final counter value: %d\n", counter_get_value());
return 0;
}
编译命令:
gcc -o static_example static_example.c
./static_example
// combined_example.c
#include <stdio.h>
#include <stdint.h>
// 嵌入式风格:硬件抽象层
typedef struct {
volatile uint32_t status; // 硬件可修改
volatile uint32_t data; // 硬件可修改
const uint32_t id; // 只读,硬件设置
} hardware_register_t;
// 模拟硬件寄存器
static hardware_register_t sim_reg = {
.status = 0x01,
.data = 0x00,
.id = 0x12345678
};
// 模块内部状态
static int initialized = 0;
static uint32_t last_data = 0;
// 初始化函数
static void init_hardware() {
if (!initialized) {
printf("[HAL] Initializing hardware...\n");
printf("[HAL] Device ID: 0x%08X\n", sim_reg.id);
initialized = 1;
}
}
// 读取数据
const uint32_t* read_data() {
if (initialized) {
last_data = sim_reg.data;
return &last_data;
}
return NULL;
}
// 写入数据
void write_data(uint32_t value) {
if (initialized) {
sim_reg.data = value;
printf("[HAL] Wrote data: 0x%08X\n", value);
}
}
// 检查状态
int check_status(uint32_t mask) {
return (sim_reg.status & mask) != 0;
}
int main() {
printf("=== Combined Example ===\n\n");
// 初始化
init_hardware();
init_hardware(); // 第二次调用不会重复初始化
// 读取数据
const uint32_t *data = read_data();
if (data) {
printf("[Main] Read data: 0x%08X\n", *data);
// *data = 0xFF; // 编译错误:不能修改const数据
}
// 写入数据
write_data(0xCAFEBABE);
// 检查状态
printf("[Main] Status bit 0: %d\n", check_status(0x01));
// 模拟硬件修改状态
sim_reg.status = 0x03;
printf("[Main] Status bit 1: %d\n", check_status(0x02));
return 0;
}
编译命令:
gcc -o combined_example combined_example.c
./combined_example
const指针的声明位置
const int *p1; // 指向常量的指针
int const *p2; // 同上(C语言中)
int *const p3; // 常量指针
const在*左边:指向常量的指针const在*右边:常量指针const与强制类型转换
const int *p;
int *q = (int*)p; // 可以强制转换,但修改是未定义行为
const正确性
volatile不是原子操作
volatile int counter = 0;
counter++; // 不是原子的!需要加锁
volatile不能替代内存屏障
volatile的常见误用
volatile int x = 10;
int y = x + 1; // 每次都从内存读取x,但计算结果可能被优化
静态变量的初始化
静态变量的线程安全性
静态变量的调试
// 错误1:误用const
const int *ptr = &value;
int *bad_ptr = ptr; // 编译警告(C++中是错误)
*bad_ptr = 10; // 未定义行为
// 错误2:volatile误用
volatile int flag = 0;
while (!flag); // 正确:检查volatile变量
// 但编译器可能优化成:
// int temp = flag;
// while (!temp); // 错误!
// 错误3:static变量作用域误解
void func() {
static int x = 0;
x++;
}
// x在函数外不可访问
答:
答: 硬件寄存器的值可能随时被硬件改变(如状态寄存器)。如果不用volatile,编译器会将寄存器值缓存到寄存器中,导致程序读取到过时的值。volatile确保每次访问都从实际内存地址读取。
答:
const int *ptr:指向常量的指针,不能通过ptr修改值,但ptr可以指向其他地方int *const ptr:常量指针,ptr本身不能修改(必须初始化),但可以通过ptr修改值答:
不安全。volatile只保证每次访问都从内存读取,但不能保证操作的原子性。例如volatile int counter++;包含读取、修改、写入三个步骤,多线程环境下可能导致竞态条件。需要配合互斥锁或原子操作使用。
答:
答:
// 好的做法:使用const保护只读数据
void process(const int *input, int *output, int size);
void print_string(const char *str);
// 不好的做法:没有使用const
void process(int *input, int *output, int size);
const不仅提供编译时保护,也向调用者表明该参数不会被修改。
答:
// 硬件寄存器:volatile + const
volatile const uint32_t * const STATUS_REG = (uint32_t*)0x40000000;
// 模块内部状态:static
static int module_state = 0;
// 配置参数:const
static const config_t default_config = {
.baudrate = 9600,
.parity = 0
};