const 类型限定符
C 类型系统中每一个独立的类型,都有该类型的几个限定版本,对应 const
、volatile
及对于指向对象指针的 restrict
限定符中的一个、两个或全部三个。此页面描述 const
限定符的效果。
编译器可以把声明为带 const 限定类型的对象放到只读内存中,并且若程序从来不获取该 const 对象的地址,则可能完全不存储它。
对类型被 const 限定的对象的任何修改尝试都导致未定义行为。
const int n = 1; // const 限定类型的对象 int* p = (int*)&n; *p = 2; // 未定义行为
const
语义仅适用于左值表达式;只要在不要求左值的语境中使用 const 左值表达式,就会丢失其 const
限定符(注意不丢失 volatile
限定符,若它存在)。
指代 const 限定类型对象的左值表达式,和指代拥有至少一个 const 限定类型成员(包含为聚合体或联合体所递归含有的成员)的结构体或联合体的左值表达式,不是可修改左值。具体而言,它们不可赋值:
const int n = 1; // const 类型对象 n = 2; // 错误: n 的类型为 const 限定 int x = 2; // 未限定类型的对象 const int* p = &x; *p = 3; // 错误:左值 *p 的类型为 const 限定 struct {int a; const int b; } s1 = {.b=1}, s2 = {.b=2}; s1 = s2; // 错误:s1 的类型无限定,但它有 const 成员
const 限定的结构体或联合体类型的成员,取得它所属类型的限定版本(在用 .
运算符或 ->
运算符访问时)。
struct s { int i; const int ci; } s; // s.i 的类型为 int,s.ci 的类型为 const int const struct s cs; // cs.i 和 cs.ci 的类型都是 const int
若以 const 类型限定符声明数组类型(通过使用 typedef),则数组类型无 const 限定,但其元素类型有。 |
(C23 前) |
始终认为数组类型与其元素类型同等地拥有 const 限定。 |
(C23 起) |
typedef int A[2][3]; const A a = {{4, 5, 6}, {7, 8, 9}}; // const int 的数组的数组 int* pi = a[0]; // 错误: a[0] 拥有 const int* 类型 void *unqual_ptr = a; // C23 前 OK ; C23 起错误 // 注: clang 即使在 C89-C17 模式也应用 C++/C23 中的规则
若以 const 类型限定符声明函数类型(通过使用 typedef),则行为未定义。
函数声明中,关键词 以下两条声明声明相同函数: void f(double x[const], const double y[const]); void f(double * const x, const double * const y); |
(C99 起) |
const 限定的复合字面量不必指代相异的对象;能与恰好拥有重叠表示的其他复合字面量和字符串字面量一同存储它们。 const int* p1 = (const int[]){1, 2, 3}; const int* p2 = (const int[]){2, 3, 4}; // p2 的值可等于 p1+1 _Bool b = "foobar" + 3 == (const char[]){"bar"}; // b 的值可为 1 |
(C99 起) |
指向非 const 类型的指针能隐式转换成指向同一或兼容类型的 const 限定版本的指针。需要转型表达式进行逆向转换。
int* p = 0; const int* cp = p; // OK:添加限定符(int 到 const int) p = cp; // 错误:舍弃限定符(const int 到 int) p = (int*)cp; // OK:转型
注意指向指向 T
指针的指针不可转换为指向指向 const T
指针的指针;对于要兼容的二个类型,其限定必须等同。
char *p = 0; const char **cpp = &p; // 错误:char* 与 const char* 不是兼容类型 char * const *pcp = &p; // OK:添加限定符(char* 到 char *const)
关键词
注解
C 从 C++ 接纳了 const 限定符,但不同于 C++,C 中 const 限定类型的表达式不是常量表达式;它们不可用作 case 标号,或用于初始化静态和线程存储期对象,用作枚举项,或位域大小。以之为数组大小时,产生的数组为 VLA。
引用
- C17 标准(ISO/IEC 9899:2018):
- 6.7.3 Type qualifiers (第 87-90 页)
- C11 标准(ISO/IEC 9899:2011):
- 6.7.3 Type qualifiers (第 121-123 页)
- C99 标准(ISO/IEC 9899:1999):
- 6.7.3 Type qualifiers (第 108-110 页)
- C89/C90 标准(ISO/IEC 9899:1990):
- 6.5.3 Type qualifiers