一般来说,const
完全由编译器控制。 当您声明某个内容为常量时,编译器 会对您的写作进行限制。它不会允许您对常量标量进行赋值,通过常量引用或指针进行赋值,或调用常量对象的非常量函数。
并不能保证编译器会提供任何形式的运行时保护。
const
关键字在C和C++中有两种不同的语义含义。
(1) 它可以声明一个对象的常量性。
const SomeType t;
t
是一个不可修改的对象。编译器会遵循“const-correctness”规则(在C和C++中不同),尽力防止您修改它。这些规则仅在语言层面上概念性地强制执行,这意味着有方法可以规避这些规则,并且这也意味着对象的const属性不一定实现在物理层面上。即没有保证该对象最终将被放置在只读存储器中。mutable
成员)。const SomeType *p;
p
被声明为指向常量的指针。这并不一定意味着p
所指向的对象是一个常量对象(由第一种const
定义)。它很可能是一个非常量对象,在这种情况下,从上述访问路径中去除constness并修改对象是完全合法的,尽管通常不是一个好的编程实践。换句话说,访问路径的constness是可以被移除的。const int* const* const* const p = 0;
包含两种不同的const
:最后一个const
声明了对象p
的常量性(第一种),而其余的const
声明了由p
表示的访问路径的各个级别的常量性(第二种)。
P.S. 作为[可能无关的]附带说明,值得注意的是,在C和C++中,术语constant具有截然不同的含义。在C++中,constants是声明为const
的对象。在C中,constants是字面值。在C的术语中,声明为const
的对象不是constants。
const int* const* const* const p = 0;
这样的声明时,这个“规则”很有用 - 基本上,“const”应用于其左侧的任何内容(除非左侧没有任何内容,在这种情况下它将应用于其右侧的任何内容)。 - Lazerint const *const *const *const p = 0;
。 - Mateen Ulhaqconst
关键字,其他回答中已经提到,有时使用它可以让编译器将这些数据放置在二进制和内存的只读区域中。根据 Ulrich Drepper 的文章《如何编写共享库》中的第2.4.2节“永远 const
”,这样做可能允许程序(1)使用更少的资源和(2)更快地启动。const
属性会导致未定义的行为,与通常情况下一样。编译器在编译代码时会计算每个表达式的类型,以便对其进行类型检查并正确地生成代码(例如,在尝试将int存储在指针中时发出警告,或正确地将整数转换为双精度浮点数)。const
可以被视为类型的一部分。由于它具有表达式的类型信息,因此它可以检查lvalue(赋值操作符左侧)的类型,并在其类型中包含“const”时抛出错误。
在优化编译器中,这实际上是一件非常复杂的事情。不过,它起初很简单。
当您使用const关键字声明变量时(让我们忽略指针,因为它们可以是const或指向const或两者都是),编译器会记住不应更改该变量的任何代码(几乎)。如果编译器看到更改const变量的代码,则认为这是一个错误(或仅值得警告,但为了简单起见,我将忽略它)。编译器还假定它看不到的任何代码(现在或以后{其他.c文件中的代码或可能是库或.s或.asm文件)都不会更改const变量(除非它是const volatile
,在这种情况下,它将假定它随时可能发生更改,但仍将强制不允许您的代码更改它--这对于用于读取设备状态但无法写入的内存映射SFR [特殊功能寄存器]非常有用。请记住,c和c ++用于操作系统和嵌入式编程)。
extern const int foo; // note that the value isn't visible, so a load is necessary
...
extern int baz(int, int);
...
int bar(int x, int y) {
int m, n;
int r = x / foo; // this would require loading the value of foo from RAM
m = baz(r, y); // the compiler normally has to assume that a function could change a global
n = m + r + foo; // but since the global foo is const it shouldn't be able to be changed
// by the call to baz and does not need to be reloaded
return n;
}
一个可执行文件(或其他目标文件)可能有一个只包含常量的节(.rodata)。在许多情况下,操作系统可能会强制不允许程序写入此数据(在某些情况下甚至可能在ROM中)。该节还可以包含非const变量的版本,用于初始化,因为它可以包含任何常量,而不仅仅是声明为const的常量。
因此,在C语言中,const主要告诉编译器告诉你,你已经犯了错误,并且正在尝试更改不应更改的东西。虽然它允许基于它做出一些假设。
在C++中,它变得更加复杂。我不记得所有细节,但我确实记得你可以根据传递给它的值是否为const来重载函数名,这可能很有用。
在C语言中,const关键字会使变量成为不可变的,也就是说它不能被修改。
通常情况下,这是一个编译时的区别,在运行时对变量进行修改没有任何影响。例如,可以使用指向相同内存地址的指针间接地修改const变量。
在C++中,const关键字有多重含义。
例如,类成员函数可以被声明为“const”,这意味着它不允许修改类实例的状态。
与C语言类似,使用指针、"mutable"关键字或const_cast<>操作符也可以间接地修改声明为const的变量。