const 与指针
const 与指针的组合可以限制指针本身或指针指向的内容的修改。理解 const int *p、int *const p 和 const int *const p 的区别,是写出安全、自文档化代码的关键。
指向常量的指针
const int *p(或 int const *p):指针指向的内容不能通过该指针修改,但指针本身可以指向别处:
int x = 10, y = 20;
const int *p = &x; /* p 指向 const int */
/* *p = 20; */ /* 错误:不能通过 p 修改 */
p = &y; /* 合法:p 可以指向别处 */
printf("%d\n", *p); /* 20 */
p 指向的内存本身不一定是常量(x 和 y 都不是 const),只是不能通过 p 修改。
常量指针
int *const p:指针本身是常量,不能指向别处,但指向的内容可以修改:
int x = 10, y = 20;
int *const p = &x; /* p 永远是 x 的地址 */
*p = 20; /* 合法:修改 x */
/* p = &y; */ /* 错误:p 不能指向别处 */
指向常量的常量指针
const int *const p:指针本身不能修改,指向的内容也不能通过该指针修改:
int x = 10;
const int *const p = &x; /* p 永远指向 x,不能通过 p 修改 x */
/* *p = 20; */ /* 错误 */
/* p = NULL; */ /* 错误 */
记忆技巧
从右往左读:
const int *p; /* p 是指针,指向 const int */
int *const p; /* p 是 const 指针,指向 int */
const int *const p; /* p 是 const 指针,指向 const int */
const 在 * 左边:指向的内容是常量 const 在 * 右边:指针本身是常量
实际应用
函数参数保护:
/* 承诺不修改 arr */
void print_array(const int arr[], int n);
/* 承诺不修改 str */
size_t strlen(const char *str);
/* 承诺不修改 src */
char *strcpy(char *dest, const char *src);
防止意外修改:
void process(const struct Data *data)
{
/* 编译器确保这里不会修改 data 指向的内容 */
printf("%d\n", data->value);
/* data->value = 10; */ /* 编译错误 */
}
去除 const
可以强制去除 const,但修改原本是 const 的对象是未定义行为:
const int x = 10;
int *p = (int *)&x; /* 去除 const */
*p = 20; /* 未定义行为!x 可能是只读的 */
去除 const 应极其谨慎,只在确实需要且保证安全时使用。
常见错误
const 位置错误:
/* 想声明指向常量的指针 */
int const *p; /* 正确:指向 const int */
const int *p; /* 同上 */
/* 错误理解 */
int *const p; /* 这是常量指针,不是指向常量 */
试图修改 const 对象:
const int x = 10;
int *p = &x; /* 警告:丢弃 const */
*p = 20; /* 未定义行为 */
最佳实践
- 函数参数中,不修改的指针用
const修饰 - 从右往左读
const指针声明 - 不要去除
const后修改原对象 const是接口契约,帮助编译器检查错误- 用
const提高代码可读性和安全性