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;
}
6条答案
按热度按时间jvidinwx1#
一般来说,
const
是100%编译器。当你声明一个常量时,编译器会对你写的东西设置限制。它不允许你赋值给常量标量,通过常量引用或指针赋值,或者调用常量对象的非常量函数。不能保证编译器会安排任何类型的运行时保护。
dy2hfwbg2#
const
关键字在C和C++中有两种不同的语义。**(1)**它可以声明一个 * 对象的常量 *
在上面的例子中,对象
t
是一个不可修改的对象。编译器将通过遵守 const-correctness 规则(这在C和C中是不一样的)来尽最大努力防止您修改它。const-correctness规则只是在概念上,在语言级别上强制执行,这意味着有办法规避这些规则,这也意味着对象的constness不一定在物理级别上实现。也就是说,不能保证对象最终会被放置在只读存储器中。重要的是要注意,这种constance是 * 不可移除的 *,在某种意义上,任何试图通过抛弃constance来修改上述对象的尝试都会导致未定义的行为(排除C中可能的
mutable
成员)。**(2)**它可以声明到对象的 * 访问路径 * 的constance
上面的
p
被声明为指向常量的指针。这并不一定意味着p
所指向的对象是常量对象(如上面第一种const
所定义的)。它可能很容易是一个非常量,在这种情况下,从上述访问路径中丢弃常量并修改对象是 * 完全法律的 *,尽管这通常不是一个好的编程实践。换句话说,访问路径的恒定性可能是 * 可移除的 *。考虑到上述情况,
包括两种不同类型的
const
:最后一个const
声明了对象p
(第一类)的恒定性,而const
的其余部分声明了由p
(第二类)表示的各级访问路径的恒定性。P.S.作为一个[可能不相关的]旁注,可能值得注意的是,术语 constant 在C和C中有着截然不同的含义。在C中,* 常量 * 是声明为
const
的对象。在C * 中,常量 * 是字面量。声明为const
的对象在C术语中不是 * 常量 *。t5zmwmid3#
除了通过使用
const
关键字提供的编译时强制不变性之外,使用它有时允许编译器将这些数据放在二进制文件和内存的 read-only 部分。根据Ulrich Drepper的文章How To Write Shared Libraries中的第2.4.2节“Foreverconst
“,这样做可能会使程序(1)使用更少的资源和(2)启动更快。请注意,像往常一样,在内存的只读区域中丢弃数据的
const
属性会导致未定义的行为。nr7wwzry4#
当编译器编译代码时,它会计算每个表达式的类型,这样它就可以对它们进行类型检查并正确地发出代码(例如,当你试图在指针中存储一个int时发出警告,或者正确地将一个整数转换为一个double)。
const
-ness可以被认为是类型的一部分。因为它有表达式的类型信息,所以它可以检查 lvalue 的类型(赋值的左侧),如果类型中有'const',则抛出错误。euoag5mw5#
在C中,const关键字将使变量不可变,这意味着它不能被修改。
通常,这是编译时的区别,对变量的运行时修改没有影响。例如,可以使用指向同一内存地址的指针间接修改const变量。
在C++中,const关键字有多种含义。
例如,类成员函数可以是“const”,这意味着不允许修改类示例的状态。
与C语言一样,声明为const的变量也可以使用指针间接修改,但也可以使用“mutable”关键字或const_cast<>运算符。
ioekq8ef6#
这在优化编译器中实际上是一件非常复杂的事情。不过,一开始很简单。
当你用const关键字声明一个变量时(让我们忽略指针,因为它们可以是const或指向const或两者兼而有之),编译器会记住任何代码都不应该改变这个变量(几乎)。如果编译器看到代码改变了一个常量变量,那么它会认为这是一个错误(或者偶尔只值得警告,但为了简单起见,我会忽略它)。编译器还假设没有它看不到的代码(无论如何(其他.c文件或可能的库或.s或.asm文件中的代码)将更改const变量(除非它是
const volatile
,在这种情况下,它会假设它可以随时更改,但仍然会强制不让您的代码更改它-这对于用于读取设备状态的内存MapSFR [特殊功能寄存器]很有用,但不能写入。请记住,c和c++用于操作系统和嵌入式编程)。假设变量在某些或所有情况下都不会改变,这允许编译器的优化例程做一些它无法做的事情。这意味着将变量的文字值放入指令流,而不是加载变量的地址,然后加载变量的值。它还可以假设,如果它加载了,如果:
可执行文件(或其他目标文件)可能有一个节(.rodata),其中只有常量。在许多情况下,操作系统可能会强制不允许程序写入此数据(或者在某些情况下甚至可能在ROM中)。这个部分也可能包含用于初始化的非const变量的版本,因为它可能包含任何常量,而不仅仅是声明为const的常量。
所以在C中,const主要是告诉编译器告诉你,你已经搞砸了,并试图改变一些不应该改变的东西。不过,它可以根据它做出一些假设。
在C++中,它变得更加复杂。我不记得所有的细节,但我记得你可以根据你传递给它的值是否是const来重载函数名,这可能很有用。