C语言中const的详细用法
2025-11-29 03:57:47
C语言中const关键字的深度解析
const是C语言中用于定义常量的关键字,它使变量成为只读状态,防止程序意外修改重要数据。正确使用const能显著提高代码的健壮性、可读性和安全性。
一、基础用法:定义常量
1. 基本常量定义
const int MAX_SIZE = 100; // 整型常量
const float PI = 3.14159; // 浮点常量
const char NEWLINE = '\n'; // 字符常量
2. 常量与宏定义的区别
特性
const常量
#define宏
类型检查
有
无(预处理器替换)
作用域
块作用域
文件作用域
调试可见性
调试器中可见
预编译后消失
内存分配
占用内存空间
不占用内存
数组大小定义
C99前不能用于数组大小
可用于数组大小
// const定义(C99前不合法,C99后合法)
const int SIZE = 10;
int arr[SIZE]; // C99前错误,C99后正确
// 宏定义(始终合法)
#define SIZE 10
int arr[SIZE]; // 始终正确
二、高级用法:指针与const
const与指针结合使用有4种组合方式,每种都有不同的保护级别:
1. 指向常量的指针(Pointer to constant)
int value = 10;
const int *ptr = &value; // ptr指向的内容不可变
// *ptr = 20; // 错误:不能通过ptr修改value
value = 20; // 正确:value本身不是常量
ptr = NULL; // 正确:指针本身可变
2. 常量指针(Constant pointer)
int a = 5, b = 10;
int *const ptr = &a; // ptr本身不可变
*ptr = 15; // 正确:可以修改指向的值
// ptr = &b; // 错误:不能改变指针指向
3. 指向常量的常量指针(Constant pointer to constant)
const int value = 100;
const int *const ptr = &value; // 指针和内容都不可变
// *ptr = 200; // 错误:不能修改内容
// ptr = NULL; // 错误:不能修改指针
4. 常量指针指向变量(较少用)
int data = 42;
int *const ptr = &data; // 指针不变,内容可变
*ptr = 99; // 正确
// ptr = &data2; // 错误
三、const在函数中的应用
1. 保护函数参数
// 保护原始数据不被修改
void print_string(const char *str) {
// str[0] = 'A'; // 错误:不能修改
printf("%s\n", str);
}
// 保护指针不被修改
void init_array(int *const arr, int size) {
// arr = NULL; // 错误:不能修改指针
for(int i=0; i arr[i] = 0; } } 2. 返回const指针 // 返回指向常量的指针 const char *get_error_message(int code) { static const char *messages[] = { "Success", "Invalid input", "Out of memory" }; return messages[code]; } // 使用 const char *msg = get_error_message(1); // msg[0] = 'X'; // 错误:不能修改 四、const与复合数据类型 1. 结构体中的const struct Point { int x; int y; }; // 整个结构体为常量 const struct Point origin = {0, 0}; // origin.x = 1; // 错误 // 结构体成员为常量 struct ImmutablePoint { const int x; const int y; }; struct ImmutablePoint p = {10, 20}; // p.x = 30; // 错误 2. 数组中的const // 常量数组 const int days[] = {31,28,31,30,31,30,31,31,30,31,30,31}; // days[1] = 29; // 错误 // 指向常量数组的指针 const char *const months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; // months[0] = "January"; // 错误 // months = NULL; // 错误 五、const的底层机制 1. 存储位置 全局const变量:通常存储在只读数据段(.rodata) 局部const变量:存储在栈上,但编译器阻止修改 动态分配的const:存储在堆上,但语义上只读 2. 强制修改(危险操作) const int immutable = 100; int *ptr = (int*)&immutable; // 强制转换去除const *ptr = 200; // 未定义行为!可能崩溃或无效修改 printf("%d vs %d\n", immutable, *ptr); // 可能输出:100 vs 200(编译器优化导致) 六、最佳实践与常见陷阱 1. 最佳实践 // 1. 函数参数:指针参数应加const保护 size_t strlen(const char *s); // 2. 硬件寄存器映射 volatile const uint32_t *HW_REGISTER = (uint32_t*)0xFFFF0000; // 3. 跨文件共享常量(头文件中) // config.h extern const int MAX_USERS; // config.c const int MAX_USERS = 1000; 2. 常见错误 // 错误1:忘记初始化 const int MIN_VALUE; // 错误:未初始化常量 // 错误2:修改const指针内容 const int arr[] = {1,2,3}; int *p = arr; // 警告:丢弃const限定符 *p = 4; // 运行时错误(写入只读内存) // 错误3:返回局部const指针 const char *get_name() { const char name[] = "Alice"; // 局部数组 return name; // 返回悬垂指针! } 七、const在嵌入式开发中的特殊应用 1. 只读硬件寄存器 // 定义只读硬件寄存器 #define HW_VERSION (*(volatile const uint32_t *)0x400FE000) void print_version() { printf("Hardware Version: 0x%08X\n", HW_VERSION); } 2. 配置表保护 // 存储在Flash中的配置表 const struct { uint16_t baud_rate; uint8_t parity; uint8_t stop_bits; } UART_CONFIG = {115200, 0, 1}; // 防止意外修改 void init_uart() { setup_uart(UART_CONFIG.baud_rate, UART_CONFIG.parity, UART_CONFIG.stop_bits); } 八、const与类型限定符组合 1. const + volatile // 只读但可能被外部改变的硬件寄存器 volatile const uint32_t *STATUS_REG = (uint32_t*)0x40005000; uint32_t current_status = *STATUS_REG; // 每次读取最新值 // *STATUS_REG = 0; // 错误:const禁止写入 2. const + static // 文件内可见的常量 static const int INTERNAL_CONST = 42; // 函数内持久化的常量 void counter() { static const int MAX_COUNT = 100; // 只初始化一次 static int count = 0; if(++count > MAX_COUNT) reset(); } 总结:const的核心价值 安全性:防止意外修改关键数据 可读性:明确标识不应修改的值 优化提示:帮助编译器进行优化 接口设计:清晰表达函数参数和返回值的意图 硬件交互:安全访问只读硬件资源 graph TD A[const用法] --> B[基本常量] A --> C[指针保护] A --> D[函数参数] A --> E[返回值] A --> F[复合类型] C --> C1[指向常量的指针] C --> C2[常量指针] C --> C3[双重const] F --> F1[结构体常量] F --> F2[常量数组] F --> F3[const成员]