我已经回答了问题@C 中的const和volatile指针有什么区别? 我理解的解释是:
const 修饰符意味着这段代码不能改变变量的值,但这并不意味着不能通过这段代码之外的方式改变该值。但是,volatile 表示“此数据可能被其他人更改”,因此编译器不会对该数据做出任何假设。
这意味着两种类型的变量都可以被外部事件改变。
但是,那么 const 和 volatile 的使用区别在哪里呢?
在 C 中,编译器优化是否适用于 const?
我已经回答了问题@C 中的const和volatile指针有什么区别? 我理解的解释是:
const 修饰符意味着这段代码不能改变变量的值,但这并不意味着不能通过这段代码之外的方式改变该值。但是,volatile 表示“此数据可能被其他人更改”,因此编译器不会对该数据做出任何假设。
这意味着两种类型的变量都可以被外部事件改变。
但是,那么 const 和 volatile 的使用区别在哪里呢?
在 C 中,编译器优化是否适用于 const?
volatile 和 const 在很多方面都不同,它们是两个截然不同的特性。
像 const 一样声明一个变量绝不意味着“我希望在程序之外修改这个变量”,我不确定你从哪里得到这个想法。如果您希望在代码之外修改 const 变量,则必须将其声明为,volatile const
否则编译器可能会假定该变量永远不会更改。
默认情况下,普通的 const 变量就像任何类型的变量一样,它们根本不能被程序本身修改。
就像普通变量一样, const 变量的行为很大程度上取决于它们声明的范围。大多数情况下,它们是在文件范围内声明的,然后它们表现为具有静态存储持续时间的其他变量,除非它们(可能)保存在内存的不同部分。如果它们是在本地范围内声明的,它们可能会在调用它们所在的函数时不时更改。
所以有很多可以优化 const 变量的情况。一种常见的优化是“字符串池”,编译器检查相同的常量字符串文字是否在代码中出现两次,然后为它们使用相同的地址。如果您希望从外部源更改此类字符串,但没有将它们声明为易失性,您会遇到奇怪的错误。
至于 volatile 变量,它们可能被外部源修改,但也可能被程序修改,这与 const 变量不同。
具有const
-qualified 类型的对象与您可以在程序中声明的其他对象一样,只是您无权修改它们。底层对象可能会因别名而改变,编译器必须小心,如果此类事件可能发生,所有其他对象也是如此。例如
void toto(double const* pi, double* x) {
printf("%g %g\n", *pi, *x);
printf("%g %g\n", *pi, *x);
*x = 5.0;
printf("%g %g\n", *pi, *x);
}
在这里,在函数内部调用toto
类似的东西并指向相同的内存是完全可以的。第二个编译器可以假设,因为它没有同时存储 和 的值没有改变。但是对于第三个,它无法预见是否已更改,因此它必须从内存中重新加载该值。toto(&a, &a)
pi
x
printf
*pi
*x
printf
*pi
volatile
与此不同。
void tutu(double volatile* pi, double* x) {
printf("%g %g\n", *pi, *x);
printf("%g %g\n", *pi, *x);
}
在这里,第二个printf
和以前一样,编译器可以假设它*x
没有改变,但*pi
它必须假设它可以并且必须从内存中重新加载它。用例volatile
在日常程序员生活中要少得多,它们主要涉及对象
setjmp/longjmp
机制下'const' 告诉编译器该值永远不会改变,不是由程序改变,也不是由其他人改变。当某些东西是 const 时,编译器会相应地优化代码,并且通常会将变量替换为代码中的常量。所以即使外面发生了变化,程序也可能永远不会知道。
'volatile'相反,告诉编译器变量可以随时从外部改变,然后编译器不会进行诸如将var放入寄存器中的优化,而是总是从内存中读取它,以防万一改变了。
演示 const 的示例
function1()
{
int i = 10;
function2(&i);
}
function2(int const *ptr) // usage of const
{
*ptr = 20; //will cause error; outside function can't be modify the value of i
}
易失性的示例
function1()
{
while(1)
{
i = 20;
print(i);
}
}
function2()
{
i = 20;
while(1)
{
print(i);
}
}
考虑这两个功能。两者似乎都是一样的。用于优化编译器将 function1 转换为 function2。问题是如果 i 的值被另一个线程改变了,那么两个函数就会变得不同,这里 while 循环打印 i 的值和另一个模块改变 i 的值。所以我们不会总是将 i 的值设为 20。
volatile 用于通知编译器不要优化变量。
const 修饰符意味着这段代码不能改变变量的值,但这并不意味着不能通过这段代码之外的方式改变该值。
有两种不同的方式来应用 const 限定符。
程序不得修改const 限定对象,否则程序具有未定义的行为。对象可以const volatile
由操作系统/硬件/任何东西修改,但不能由程序分配。为免生疑问,const 对象是其定义使用 const 类型的对象。
指向 const-qualified-type 的指针防止(在编译时)通过该指针进行修改,但可以使用指向同一对象的其他指针来修改它。如果对象本身不是 const,则定义行为。但是,编译器可能仍然假设只有程序修改了对象,从而考虑了 OS/hardware/whatever require 的任意修改volatile
。
就通过其他指针进行的修改而言,指向非 const 限定类型的指针与指向 const 的指针完全相同。
但是,volatile 表示“此数据可能会被此程序中的代码以外的其他内容更改”,因此编译器在优化时不会对该数据做出任何假设。
所以区别如下:
#include <stdio.h>
void some_other_function(const int *);
int main() {
int a = 0;
int volatile b = 0;
int const c = 0;
int const *constptr = &a;
int *ptr = (int*) constptr;
printf("%d\n", a); // compiler can assume a == 0 at this point, and
// replace the code with puts("0") if it wants to
printf("%d\n", b); // compiler cannot assume a value for b, it's volatile[*]
some_other_function(constptr); // defined in another TU
printf("%d\n", a); // compiler can *no longer* assume that a == 0,
// it might have changed
*ptr = 1; // there's another example of a changing, legally
some_other_function(&c);
printf("%d\n", c); // compiler can assume c == 0 because c is const
}
[*] 虽然我说它“不能假设一个值”,但可能是某些假设的 C 实现碰巧知道没有操作系统或硬件机制可以通过任何需要volatile
检测的方式修改自动变量。尤其是在这种情况下,没有对函数的引用b
已经转义。如果是这样,那么您可能会发现实现实际上可以volatile
在此特定代码中忽略,但也许它volatile
“正确”处理外部全局变量,因为它知道链接器提供了一种将它们映射到 I/O 端口地址或其他地址的方法.
在下面的情况下,可以很容易地看出 Volatile 和 Const 之间的区别,
1) 如果您说某个变量为 Const,则您的程序可能无法修改。
2)如果你说volatile,它只是在提示编译器不要优化代码,因为值可能会从外部线程或其他程序中改变。
3)如果我们定义一个变量为Const Volatile,则表示该变量不能被同一个程序修改,不会被编译器优化,可以被外部线程或程序修改。
例子:
如果我写一个像下面这样的函数,
const freq = 10;
calfreq()
{
return (Const freq * 2);
}
在这种情况下,编译器可能会将代码优化为
return(20);
每时每刻。
但在我的情况下,频率值可能会改变,因为外部硬件/线程/程序所以,如果我说常量易变,那么问题将得到解决。