摘要:
const int *p与int const *p含义相同,表示「通过指针不能修改所指对象」;int * const p表示「指针本身不可再指向别处」。本文用经典与拓展示例说明差异,并给出工程实践建议。
关键词: C 语言、const、指针、只读视图、常量指针
一、核心结论(速查)
| 写法 | 一句话含义 |
|---|---|
const int *grape |
指向只读 int 的指针 :不能通过 grape 修改那个 int,但 grape 可以指向别的地址。 |
int const *grape |
与上一行完全相同 ,仅 const 与类型名的书写顺序不同。 |
int * const grape_jelly |
常量指针,指向可改 int :grape_jelly 不能再指向别处,但可以通过它修改指向的 int。 |
记忆口诀(看 * 与 const 的相对位置):
const在*左边 → 数据只读;const在*右边 → 指针只读
二、const int * 与 int const *(等价)
2.1 语义
指针的类型是「指向 const int」,即 通过该指针不能修改所指内存中的 int 。
这不代表「内存里一定是常量」------对象本身可能是普通变量,只是 当前这个指针通道不允许写。
2.2 经典例子:只读函数参数
标准库中大量接口用此约定表达「只读缓冲区」:
c
size_t strlen(const char *s); /* 实现中承诺不通过 s 修改字符串 */
void print_values(const int *p, size_t n)
{
size_t i = 0;
for (i = 0; i < n; i++) {
printf("%d ", p[i]); /* 可以读 */
/* p[i] = 0; */ /* 编译错误:不能通过 p 写 */
}
}
2.3 经典例子:指针可改指向,不可改目标
c
void example_read_only_view(void)
{
int a = 1;
int b = 2;
const int *grape = &a;
/* *grape = 10; */ /* 错误:不能通过 grape 修改所指 int */
grape = &b; /* 正确:grape 本身不是 const 指针,可以改指向 */
}
2.4 拓展:const int * 指向非 const 变量
c
void example_alias_read_only(void)
{
int x = 5;
const int *p = &x; /* 合法:用「只读视图」观察 x */
/* *p = 1; */ /* 错误:不能通过 p 写 */
x = 1; /* 合法:仍可直接改 x(不经过 p) */
}
2.5 拓展:两种写法等价
c
const int *p1;
int const *p2; /* 与 p1 类型相同 */
团队可统一一种风格(例如始终写 const int *),避免无谓争论。
三、int * const(常量指针)
3.1 语义
指针变量本身是常量 :定义后不能再让该指针指向别的地址(必须在定义时给出合法初值,除非在特殊上下文如函数参数中另有规则)。
仍可通过该指针修改所指的 int。
3.2 经典例子:固定指向某缓冲区
c
void example_const_pointer(void)
{
int buffer[4] = {1, 2, 3, 4};
int * const grape_jelly = buffer; /* 始终指向 buffer 首元素 */
grape_jelly[0] = 99; /* 正确:修改的是 int 对象 */
*grape_jelly = 100; /* 同上 */
/* grape_jelly++; */ /* 错误:不能移动指针 */
/* grape_jelly = &buffer[1]; */ /* 错误:不能改指针的值 */
}
3.3 经典例子:函数内锁定指针形参(实现细节)
c
void bump_first(int * const p)
{
if (p == NULL) {
return;
}
(*p)++; /* 可以修改指向的 int */
/* p++; */ /* 错误:不能修改 p 本身 */
}
3.4 拓展:与数组名的类比
在多数表达式中,数组名会退化为指向首元素的指针,但数组名不是可赋值的左值:
c
void example_array_name(void)
{
int arr[3] = {0, 0, 0};
/* arr = NULL; */ /* 错误:不能对数组名整体赋值 */
}
思路上与「指针不可改指向」有相似之处,但数组名与 int * const 在标准中的规则并不完全相同,类比有助于直觉理解。
四、三种组合对照表
| 声明 | 能否改指针指向的地址 | 能否通过指针改所指 int |
|---|---|---|
const int *p |
能 | 不能 |
int * const p |
不能 | 能 |
const int * const p |
不能 | 不能 |
拓展:const int * const
c
void example_both_const(void)
{
const int x = 42;
const int * const grape = &x;
/* grape = &x; */ /* 错误:指针常量 */
/* *grape = 0; */ /* 错误:指向 const int */
}
五、如何读复杂声明
- 螺旋法则 / 先右后左 :从标识符出发,向右读,遇到
)或边界再向左读,交替进行。 - 简化规则 :
const默认修饰其左侧紧邻的类型;若左侧没有可修饰成分,则修饰右侧。
掌握后可以继续分析 const int **、int * const * 等多级指针,原则是 逐层、逐个 const 与 * 分析。