C中const&volatile限定符的用法明显区别?

我已经经历了问题的答案@C中的const和volatile指针之间有什么区别? 我理解的解释是:

const修饰符意味着此代码不能更改变量的值,但这并不意味着无法通过此代码之外的方式更改该值。 但是,volatile表示“此数据可能会被其他人更改”,因此编译器不会对该数据做出任何假设。

这意味着外部事件可以改变这两种类型的变量。

但是,那么const和volatile的使用差异在哪里?

在C中,编译器优化是否适用于const?

volatile和const在很多方面都有所不同,它们是两个截然不同的特征。

声明一个变量就像const一样意味着“我希望在程序之外修改这个变量”,我不知道你从哪里得到了这个想法。 如果您希望在代码之外修改const变量,则必须将其声明为volatile const否则编译器可能会假定变量永远不会更改。

默认情况下,普通const变量就像任何类型的变量一样,它们根本无法被程序本身修改。

就像普通变量一样,const变量行为在很大程度上取决于它们声明的范围。 大多数情况下,它们在文件范围内声明,然后它们表现为具有静态存储持续时间的其他变量,除了它们(可能)保存在内存的不同部分。 如果它们在本地范围内声明,则它们可能会不时地调用它们所在的函数。

因此,有很多情况可以优化const变量。 一个常见的优化是“字符串池”,其中编译器检查相同的常量字符串文字是否在代码中出现两次,然后对它们使用相同的地址。 如果你期望从外部源更改这些字符串,但没有将它们声明为volatile,那么你会遇到奇怪的错误。

至于volatile变量,它们可能会被外部源修改,但与const变量不同,它们也可能被程序修改。

具有const限定类型的对象是您可以在程序中声明的其他对象之类的对象,只是您无权修改它们。 底层对象可能会更改,例如通过别名,编译器必须注意,如果此类事件可能发生的话,所有其他对象。 例如

 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(&a, &a)类的东西调用toto是完全可以toto(&a, &a)因此在函数pix内部指向相同的内存。 对于第二个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); } } 

考虑这两个function。 两者似乎都是一样的。 for optimization compiler将function1转换为function2。 问题是如果i的值被另一个线程改变,那么两个函数变得不同,这里i的循环打印值和另一个模块改变i的值。 所以我们永远不会得到i的值。

volatile用于通知编译器不优化变量。

const修饰符意味着此代码不能更改变量的值,但这并不意味着无法通过此代码之外的方式更改该值。

应用const限定符有两种不同的方法。

程序不能修改const限定对象 ,否则程序具有未定义的行为。 const volatile对象可以由OS /硬件/其他任何东西修改,但不能由程序分配。 为避免疑义,const对象是一个定义使用const类型的对象。

指向const-qualified-type的指针防止(在编译时)通过该指针进行的修改,但是可以使用指向同一对象的其他指针来修改它。 如果对象本身不是const,则定义行为。 但是,编译器仍然可以假设只有程序修改对象,考虑OS /硬件/任何需要volatile任意修改。

对于通过其他指针进行修改,指向非const限定类型的指针与指向const的指针完全相同

但是,volatile表示“此数据可能会被此程序中的代码以外的其他内容更改”,因此编译器在优化时不会对该数据做出任何假设。

所以区别在于:

 #include  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的function已经逃脱了。 如果是这样,那么您可能会发现实现实际上可以忽略此特定代码中的volatile ,但它可能“正确”地处理外部全局volatile变量,因为它知道链接器提供了将它们映射到I / O端口地址的方法管他呢。

在下面的情况下,可以很容易地看出Volatile和Const之间的区别,

1)如果您将某个变量称为Const,则可能无法通过您的程序进行修改。

2)如果你说volatile,它只是给编译器提示不要优化代码,因为值可能会从外部线程或其他程序改变。

3)如果我们将变量定义为Const Volatile,这意味着该变量不能被同一程序修改,不会被编译器优化,可以被外部线程或程序修改。

例:

如果我写下面的函数,

 const freq = 10; calfreq() { return (Const freq * 2); } 

在这种情况下,编译器可以优化代码

 return(20); 

每时每刻。

但在我的情况下,freq值可能会改变,因为外部硬件/线程/程序因此,如果我说Const Volatile,那么问题将得到修复。