2

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

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

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

但是,那么 const 和 volatile 的使用区别在哪里呢?

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

4

6 回答 6

3

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

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

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

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

所以有很多可以优化 const 变量的情况。一种常见的优化是“字符串池”,编译器检查相同的常量字符串文字是否在代码中出现两次,然后为它们使用相同的地址。如果您希望从外部源更改此类字符串,但没有将它们声明为易失性,您会遇到奇怪的错误。

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

于 2013-01-14T08:36:03.890 回答
1

具有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)pixprintf*pi*xprintf*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机制下
  • 或通过不同的线程
于 2013-01-14T10:15:35.600 回答
0

'const' 告诉编译器该值永远不会改变,不是由程序改变,也不是由其他人改变。当某些东西是 const 时,编译器会相应地优化代码,并且通常会将变量替换为代码中的常量。所以即使外面发生了变化,程序也可能永远不会知道。

'volatile'相反,告诉编译器变量可以随时从外部改变,然后编译器不会进行诸如将var放入寄存器中的优化,而是总是从内存中读取它,以防万一改变了。

于 2013-01-14T08:50:35.227 回答
0

演示 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 用于通知编译器不要优化变量。

于 2013-01-14T09:11:56.160 回答
0

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 端口地址或其他地址的方法.

于 2013-01-14T10:22:11.677 回答
0

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

1) 如果您说某个变量为 Const,则您的程序可能无法修改。

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

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

例子:

如果我写一个像下面这样的函数,

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

在这种情况下,编译器可能会将代码优化为

return(20);

每时每刻。

但在我的情况下,频率值可能会改变,因为外部硬件/线程/程序所以,如果我说常量易变,那么问题将得到解决。

于 2015-03-30T13:55:07.880 回答