const 是给编译器的「承诺」;restrict/volatile 是「优化提示」。
const 和指针结合,位置不同、意义不同。从变量名往左读最清楚:
const int *p; // p 是「指向 const int 的指针」:指向的内容不能改
int const *p; // 与上面完全相同(const 在 int 前后等价)
int *const p; // p 是「const 指针」:指针本身不能改
const int *const p; // 指针和内容都不能改
| 声明 | 读作 | 能改 *p | 能改 p |
|---|---|---|---|
const int *p | 指向常量的指针 | 否 | 是 |
int *const p | 常量指针 | 是 | 否 |
const int *const p | 指向常量的常量指针 | 否 | 否 |
int x = 1, y = 2;
const int *p = &x; // 允许:用「只读视角」看 x
// *p = 10; // 编译错误
p = &y; // 指针本身可改
int *const q = &x; // q 永远指向 x
*q = 10; // 可改内容
// q = &y; // 编译错误
const int *p不代表 x 真是常量,只代表「通过 p 这个指针你不许写」;同一块内存可有只读/可写两个视角。
把 const 指针强转成非 const 再写,是典型的自欺欺人:
const int x = 10;
int *p = (int*)&x; // 丢掉 const
*p = 20; // 未定义行为(若 x 真在只读段,直接崩)
x 是只读段/常量,写它是 UB,可能段错误。*p = 20 后的行为完全不可预期。只读参数写 const,是 C 里最实用的习惯——既防手滑,又是自文档:
void print(const char *s); // 承诺不修改 s
void copy(char *dst, const char *src); // 只改 dst,不改 src
这让调用者放心传字符串字面量,也让编译器能在只读段复用数据。
restrict(C99)告诉编译器:这个指针指向的内存,只能通过这一个指针访问,没有别的指针和它指向同一块。编译器据此做激进优化。
void add(int n, int *restrict a, const int *restrict b) {
for (int i = 0; i < n; i++) a[i] += b[i];
}
restrict 就违背了,行为未定义。a[i] 都担心影响 b[i] 的读。memcpy(原型里 restrict 让编译器用 SIMD 批量拷贝)。void *memcpy(void *restrict dest, const void *restrict src, size_t n);
// 注意:dest 和 src 重叠时该用 memmove,而不是 memcpy
volatile 和 const 相反——它告诉编译器:这个变量可能被「你之外的东西」改变,每次访问都必须真的读写内存,不许缓存进寄存器、不许省略。
volatile int *status = (int*)0x4000; // 内存映射 IO 寄存器
while (*status == 0) { /* 忙等 */ } // 每次循环都重新读内存
典型用途:
sig_atomic_t + volatile。setjmp/longjmp 之间被改的变量。注意:
volatile不是线程同步工具,不保证原子性、不建内存屏障。多线程正确同步要用_Atomic(C11)或锁,volatile只是「别优化掉这次访问」。
限定符也有层级,和指针层级对应:
int *volatile p; // p 是 volatile 指针(指针本身易变)
volatile int *p; // 指向 volatile int(指向的内容易变)
const int *const p; // 指向 const 的 const 指针
const/restrict 是纯编译期概念,进汇编后消失,只影响优化;volatile 直接阻止优化,强制生成每次访问指令。final 类似 const(引用不可改绑,对象内容仍可变,类似 T *const);没有 restrict 和 volatile 的对应物(Java 的 volatile 是内存屏障语义,完全不同)。const/volatile 语义与 C 相同,还有 constexpr;restrict 在 C++ 非标准,各家编译器用 __restrict。_name、NAME)。
const=「不许通过我写」;restrict=「只有我能访问」;volatile=「别优化、每次都读」。违约 = UB。