【问题标题】:const in body of C++ functionsC++ 函数体中的 const
【发布时间】:2013-09-28 16:55:31
【问题描述】:

我意识到,如果我将 const int 定义到 c++ 函数的主体中,然后使用地址算术来更改常量的值(它在堆栈上,不是吗?)。我得到了这个代码:

const int a = 10;
int b = 100;

int * c = &b;
c++;

cout << "&a: " << &a << endl;
cout << " c: " << c << endl;


*c = 100500;

cout << " a: " << a << endl;
cout << "*c: " << *c << endl;

我得到了这个输出

&a: 0x7ffff63866a8
c: 0x7ffff63866a8

 a: 10
*c: 100500

因此,地址相同,但值不同。有人可以解释一下这个结果吗?谢谢! p.s.我试过 GCC、Clang 和 VS

【问题讨论】:

  • 无论如何改变 const 是标准中未定义的行为。
  • 你刚刚调用了未定义的行为。
  • c 后增量的值不是定义的行为,既不是值也不是解引用。如果b 是一个数组(甚至是一个数组),值将被定义,但取消引用仍然不是(在单长的情况下)。
  • @OliCharlesworth 不是。大约一年前,当我提议使用&amp;c&amp;c+1 作为边界时,我建议通过迭代器所需的参数列表发送一个单例(实际上......我使用了与OP 相同的指针)这里)。我必须把它掸掉才能找到它,但最终标准被引用为 last+one 仅对数组类型有效。正如你想象的那样,我很惊讶。
  • @OliCharlesworth ...但我会继续挖掘。由于我在炮击后放弃了原始问题的答案,因此我只能通过评论找到它,我们知道 是多么快乐。

标签: c++ constants point-of-interest


【解决方案1】:

谁能解释一下这个结果?

与通过无效指针算术或其他恶作剧以无效方式访问对象的任何尝试一样:未定义的行为。

这里发生的是编译器假设a 的值不会改变,并将其优化为编译时常量。允许这样做,因为您已将其声明为 const 并因此声明它不会更改。

它在堆栈上,不是吗?

由于您还获取了它的地址(当您打印&amp;a 时),它确实在堆栈上分配了一个地址;但是程序不需要从该地址读取来获取值,因为它已经知道是 10。

当然,这一切都是未定义的,所以如果它为你点了披萨,程序也同样有效。

【讨论】:

  • 绝对正确。两个关键点:1)“const”值 NOT 是否需要存储在堆栈中:编译器可以对它做任何事情,2)“未定义行为”==“同样有效如果它给你点了披萨”。很好的回应:)
  • +1 表示“给你点了披萨”……我要我的意大利辣香肠!
【解决方案2】:

你的程序到处都是未定义的行为...

假设增加b 的地址会让你到达a 是错误的,它可以或它不能。然后,您将使用标准 不安全派生指针 中调用的内容来修改 const 对象 (a),这也是未定义的行为。任何事情都有可能发生。

真正发生的事情(在您的实现中,解释您的结果,但您不能依赖它,因为这是未定义的行为)是您通过以下方式强制在堆栈中分配 a获取它的地址,你得到了一个指向它的指针。您修改了该值,并更新了内存中的地址。但是,在表达式中:cout &lt;&lt; " a: " &lt;&lt; a &lt;&lt; endl; 编译器知道 a 是一个常量,因此它的值只能是 10,所以它将代码转换为 cout &lt;&lt; " a: " &lt;&lt; 10 &lt;&lt; endl; 以避免不得不去到内存中获取值。

【讨论】:

    【解决方案3】:
    const int a = 10;
    int b = 100;
    

    好的。

    int * c = &b;
    

    好的,但是很傻,我的错误计开始转动了。

    c++;
    

    Bug-o-meter 现在位于黄色区域。

    cout << "&a: " << &a << endl;
    

    好的

    *c = 100500;
    

    Bug-o-meter 挂钩。调用了未定义的行为。世界爆炸了,你被解雇了。

    c++ 将指针移动到内存中的下一个int。指针数学是可以的,但此时您可以使用c指针 与另一个指针进行比较。您不能以任何方式取消引用指针。但这就是您尝试通过它分配时所做的事情。

    接下来发生的事情无关紧要,老实说,具有误导性。您认为c 现在指向与b 相同的内存,也许它确实如此。但这只是通过未定义的行为发生的。您可能还认为 *c 的值是您所期望的,但这个结果是错误的。也许是,也许不是。当你打开盒子时,你打碎了小瓶——猫已经死了。你的程序也是如此。

    顺便说一句,如果你试图做的是想办法欺骗编译器以便你可以更改const,请不要——严格禁止更改const 无论如何。

    C++ 中有一个const_cast,但这也不是您可以用来更改const 的机制。

    【讨论】:

      【解决方案4】:

      它在堆栈上,不是吗?

      不,您的期望是错误的。 C++ 没有任何堆栈的概念,更不用说不同的自动变量如何相对于彼此存储在内存中。您正在尝试做的是简单的未定义行为。

      在您的情况下,编译器优化了a,因为标准允许它们这样做,并且您得到的结果不必有任何意义,因为它无论如何都是 UB。

      【讨论】:

        【解决方案5】:

        C++ 编译器只会假设您永远不会尝试更改 const 变量的值。

        这并不意味着如果你这样做你会得到一个错误......只是编译器作者可以忽略将要发生的事情,并且任何发生的事情都将被归类为“你的错”。

        【讨论】:

          【解决方案6】:

          SSCC:

          #include <stdio.h>
          
          int main ()
          {
            const int a = 10;
            int b = 100;
            int *c = &b;
            printf ("a=%d, b=%d, *c=%d; &a=%p, &b=%p, c=%p\n",
              a, b, *c, (void *)&a, (void *)&b, (void *)c);
          
            c++;  // "c" now invalid
            printf ("a=%d, b=%d, *c=%d; &a=%p, &b=%p, c=%p\n",
              a, b, *c, (void *)&a, (void *)&b, (void *)c);
          
            *c = 100500;  // Undefined behavior!
            printf ("a=%d, b=%d, *c=%d; &a=%p, &b=%p, c=%p\n",
              a, b, *c, (void *)&a, (void *)&b, (void *)c);
          
            return 0;
          }
          

          示例输出:

          a=10, b=100, *c=100; &a=0028FF18, &b=0028FF14, c=0028FF14
          a=10, b=100, *c=10; &a=0028FF18, &b=0028FF14, c=0028FF18
          a=10, b=100, *c=100500; &a=0028FF18, &b=0028FF14, c=0028FF18
          

          案例 2 - 我们 尝试获取 CONST A 的地址:

          #include <stdio.h>
          
          int main ()
          {
            const int a = 10;
            int b = 100;
            int *c = &b;
            printf ("a=%d, b=%d, *c=%d; &b=%p, c=%p\n",
              a, b, *c, (void *)&b, (void *)c);
          
            c++;  // "c" now invalid
            printf ("a=%d, b=%d, *c=%d; &b=%p, c=%p\n",
              a, b, *c, (void *)&b, (void *)c);
          
            *c = 100500;  // Undefined behavior!
            printf ("a=%d, b=%d, *c=%d; &b=%p, c=%p\n",
              a, b, *c, (void *)&b, (void *)c);
          
            return 0;
          }
          

          样本输出

          a=10, b=100, *c=100; &b=0028FF14, c=0028FF14
          a=10, b=100, *c=2686744; &b=0028FF14, c=0028FF18
          a=10, b=100, *c=0; &b=0028FF14, c=00018894
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-04-19
            • 2010-09-14
            • 1970-01-01
            • 1970-01-01
            • 2012-06-14
            • 2013-04-06
            相关资源
            最近更新 更多