【问题标题】:changing the value of const variable in C++ [duplicate]在 C++ 中更改 const 变量的值 [重复]
【发布时间】:2010-01-05 13:07:05
【问题描述】:

我正在尝试更改定义为 int const 的变量的值,如下所示。

const int w = 10;
int* wp = const_cast <int*> (&w);
*wp = 20;

w 的值没有改变,即使在赋值之后也是 10,尽管它表明 w 和 wp 都指向同一个内存位置。但是如果在声明时定义如下,我可以更改 w 的值

int i = 10;
const int w = i;

如果我更改 i 的声明以使其像 in 一样 const

const int i = 10;

w 的值不变。

在第一种情况下,为什么 w 的值没有改变,即使 w 和 wp 指向同一个内存位置[这是我打印它们的地址时得到的印象]

编译器对这两种情况的处理方式不同有什么不同?

有没有办法确保 w 不会失去常量,而不管它的定义方式如何?

【问题讨论】:

  • 如果一个对象最初被创建为const,那么你不能(不应该)将const_cast它设置为非const;这是未定义的行为。如果一个对象最初创建为非const,您可以随意来回const_cast
  • 在某些实现中,如果对象的内容位于只读部分中(例如,.rodata 而不是 .data,则写入 const 对象可能会使您的程序崩溃) -const 数据位)。
  • 似乎没有人提出明显的问题——如果你想改变值,为什么要把变量标记为const
  • @Graeme:不,OP 想要一种方法让const 坚持下去,即保证人们不能const_cast constness 远离他们的对象。这在问题的最后一行中说明。
  • 我不是在尝试更改值,而是在尝试了解差异以及如何禁止其他人使用我的程序。我试图发表更大的评论,但它似乎比应该发表的评论更大。将改写和评论。

标签: c++ constants


【解决方案1】:

这是未定义 const 强制转换的情况之一,因为代码可能已经过优化,因此 w 不是真正的变量,也不存在于编译的代码中。

尝试以下方法:

const volatile int w = 10; 
int &wr = const_cast <int &> (w); 
wr = 20; 
std::cout << w << std::endl;

无论如何,我不建议这样滥用 const_cast。

【讨论】:

  • 神奇的区别在于volatile 关键字。把它去掉,w=10,就像在 Narendra 最初的基于指针的示例中一样。
  • 你是对的,volatile 限定符是用来阻止编译器优化 w 的。
  • 我在三种不同的编译器[Visual C++ Express Edition、g++ 和Sun CC 编译器] 上尝试了相同的方法,在所有三种编译器上结果都是一样的。结果与所有编译器一致,因此造成混乱。顺便说一句,我尝试使用 volatile 并按上述方式工作。
  • 我的一位同事提到,只有在编译时知道值时,变量才会保持不变。当定义为 int i = 10; int 常量 w = i;因为 i 不是 const,所以我们可以在定义 w 之前通过读取用户的值来更改它的值。如果 const 变量的值是用文字或 const 变量分配的,则该值将在编译时已知并保持不变。可能是 const_cast 的目的正如 Chris Jester 所评论的那样,来回 const_cast 那些被创建为非常量而不是其他方式的对象
  • 所有 const 对象的写入都是未定义的。不确定volatile 示例应该显示什么,它仍然未定义。
【解决方案2】:

上面例子中的代码翻译成下面的汇编器:

    movl    $10, 28(%esp)  //const int i = 10; 
    leal    28(%esp), %eax //int* wp = const_cast <int*>(&i);
    movl    %eax, 24(%esp) //store the pointer on the stack
    movl    24(%esp), %eax //place the value of wp in eax
    movl    $20, (%eax) //*wp  = 20; -  so all good until here
    movl    $10, 4(%esp) //place constant value 10 onto the the stack for use in printf
    movl    $.LC0, (%esp) // load string
    call    printf //call printf

因为原来的 int i 被声明为常量,编译器保留使用文字值而不是存储在堆栈中的值的权利。这意味着该值不会改变,您会被原来的 10 所困。

这个故事的寓意是编译时常量应该保持不变,因为这就是您告诉编译器的内容。这个故事的寓意是,为了改变常数而抛弃常数会导致坏事。

【讨论】:

    【解决方案3】:

    const_cast 不会像定义的那样消除变量的 const-ness。如果您要通过引用将非常量变量传递给采用 const 引用(如 void foo(const int&amp; x))的方法,那么您可以使用 const_castfoo 中修改 x 的值,但前提是您的变量实际上传入的并不是 const。

    【讨论】:

      【解决方案4】:

      为什么不能重新绑定常量?所以不是

      const int w = 10;
      int* wp = const_cast <int*> (&w);
      *wp = 20;
      // some code
      

      只需引入不同的同名常量

      const int w = 10;
      {
         const int w = 20;
         // the same code
      }
      

      如果“新”常量应该依赖于它自己的值,您应该引入另一个常量 (const int _w = w; const int w = _w * 2;)。编译器将优化不必要的分配 - 因为我们已经看到它已经完成了这样的优化,因为这就是您提出问题的原因。

      【讨论】:

      • 丑陋的是你的const_casts 和汇编代码,而重新绑定常量是使用它们的自然方式。
      【解决方案5】:

      您不应该更改 const 值。它是 const 是有原因的,尝试更改它很可能只会导致错误。如果 const 存储在只读内存部分,那么您将遇到访问冲突。

      【讨论】:

      • 很公平,这也是我的第一反应。但在现实世界中,有时你必须这样做。 “它是 const 是有原因的,”至少假设最初将其设为 const 的程序员知道他在做什么。
      • @John:即使在现实世界中,也没有理由更改 const 值。解决根本问题,而不是乱搞。
      【解决方案6】:

      好问题。我认为混淆来自于这样一个事实,即 C++ 根据上下文使用关键字“const”来表示两个不同的概念。这些概念是常量和只读变量。

      如果在编译期间可以计算“const”变量的值,它会创建一个真正的常量。每当使用时,对此类常量的引用都会被其值替换。这就是为什么内存中没有位置可以更改以影响所有使用它的地方。这就像使用#define。

      当编译期间无法计算“const”变量的值时,它会创建一个只读变量。它在内存中有一个包含值的位置,但编译器强制执行只读行为。

      【讨论】:

        【解决方案7】:

        这是一个复习,应该注意这是在 C 中。这是使用 const 关键字使用变量或指针的看似棘手的基础。这突出了指针变量foo 之间的区别以及它的含义如何通过使用所述关键字来改变。

        字符常量 *foo; 字符 * 常量 foo; 常量字符 *foo;

        第一个和最后一个声明,使得 foo 指向的数据只读,但是,你可以改变 foo 指向的地址,例如

        常量 *char foo; /* OR char const *foo */ 字符 str[] = "你好"; foo = &str[0]; /* 好的! */ foo[1] = 'h'; /* BZZZTTT!编译失败! */

        上面的中间声明,使得指针只读,即你不能改变'foo'指向的数据的地址

        字符 * 常量 foo; 字符 str[] = "你好"; foo = &str[0]; /* BZZZTTT!编译失败! */

        【讨论】:

        • const *char foo; 是一个错误
        • char *const foo; is an error in C++ (this question is tagged C++) - const variables must have an initializer
        • 是的,我在寻找类似问题的重复项时遇到了这个线程
        【解决方案8】:

        我的猜测是声明 w const 允许编译器执行更积极的优化,例如内联 w 的值和重新排序指令。 w 是否发生变化取决于在具体情况下应用了哪些优化并且不受您的控制。

        你不能强制 w 完全是 const。 cons_cast 应该是对程序员的暗示,他们可能正在做一些可疑的事情。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-10-01
          • 1970-01-01
          • 2019-11-11
          • 1970-01-01
          相关资源
          最近更新 更多