【问题标题】:C/C++ changing the value of a constC/C++ 更改 const 的值
【发布时间】:2009-02-24 18:55:20
【问题描述】:

我有一篇文章,但我把它弄丢了。它展示并描述了一些人们应该小心的 C/C++ 技巧。其中一个对我很感兴趣,但现在我正在尝试复制它,我无法将它编译。

这个概念是可以在 C/C++ 中意外更改 const 的值

原来是这样的:

const int a = 3;          // I promise I won't change a
const int *ptr_to_a = &a; // I still promise I won't change a
int *ptr;
ptr = ptr_to_a;

(*ptr) = 5;               // I'm a liar; a is now 5

我想把这个给朋友看,但现在我错过了一步。有谁知道开始编译和工作缺少什么?

ATM 我得到从 'const int*' 到 'int*' 的无效转换,但是当我阅读这篇文章时,我尝试过,效果很好。

【问题讨论】:

  • 我发布了一个完整的程序并解释说是g++阻止了它,gcc允许这种行为。
  • 即使你让它编译。这是未定义的行为。它可能会炸毁你的计算机,让你的程序崩溃,或者让恶魔从你的鼻子里飞出来。或者,当然,它可能看起来有效。目前。在你的机器上。

标签: c++ constants


【解决方案1】:

你需要抛弃 constness:

linux ~ $ cat constTest.c
#include <stdio.h>


void modA( int *x )
{
        *x = 7;
}


int main( void )
{

        const int a = 3; // I promisse i won't change a
        int *ptr;
        ptr = (int*)( &a );

        printf( "A=%d\n", a );
        *ptr = 5; // I'm a liar, a is now 5
        printf( "A=%d\n", a );

        *((int*)(&a)) = 6;
        printf( "A=%d\n", a );

        modA( (int*)( &a ));
        printf( "A=%d\n", a );

        return 0;
}
linux ~ $ gcc constTest.c -o constTest
linux ~ $ ./constTest
A=3
A=5
A=6
A=7
linux ~ $ g++ constTest.c -o constTest
linux ~ $ ./constTest
A=3
A=3
A=3
A=3

通用答案在 g++ 4.1.2 中也不起作用

linux ~ $ cat constTest2.cpp
#include <iostream>
using namespace std;
int main( void )
{
        const int a = 3; // I promisse i won't change a
        int *ptr;
        ptr = const_cast<int*>( &a );

        cout << "A=" << a << endl;
        *ptr = 5; // I'm a liar, a is now 5
        cout << "A=" << a << endl;

        return 0;
}
linux ~ $ g++ constTest2.cpp -o constTest2
linux ~ $ ./constTest2
A=3
A=3
linux ~ $

顺便说一句.. 永远不推荐这样做...我发现 g++ 不允许这种情况发生..所以这可能是您遇到的问题。

【讨论】:

  • 试过了,但它不起作用:\ a 将保持相同的值, (*ptr) 将有另一个值
  • int 4.1.2 它在 g++ 中被阻止,但在 gcc 中没有。
  • 可能是编译器缓存了a的值。将 a 设为“const volatile a = 3;”,将“ptr”设为“volatile int *ptr;”
  • 它不能可靠地工作这一事实应该很好地说明了为什么对编译器撒谎是不好的。
  • ... 或者它只是优化这些东西,因为编译器对自己说“嗯,标准说这会产生未定义的行为。所以我会尽我所能并优化它所以我'米快”。
【解决方案2】:

只是一个猜测,但一个常见的问题是为什么不能将 int** 转换为 const int**,这乍一看似乎是合理的(毕竟,您只是添加了一个 const,这一般没问题)。原因是如果你能做到这一点,你可能会不小心修改const 对象:

const int x = 3;
int *px;
const int **ppx = &px;  // ERROR: conversion from 'int**' to 'const int**'
*ppx = &x;  // ok, assigning 'const int*' to 'const int*'
*px = 4;    // oops, just modified a const object

这是一个非常不直观的结果,但在这种情况下确保您不能修改 const 对象的唯一方法(注意没有类型转换)是使第 3 行出错。

您只能在 FIRST 间接级别添加 const 而不进行强制转换:

int * const *ppx = &px;  // this is ok
*ppx = &x;               // but now this is an error because *ppx is 'const'

在 C++ 中,如果不使用某种类型转换,就不可能修改 const 对象。您必须使用 C 样式转换或 C++ 样式 const_cast 来删除 const-ness。任何其他这样做的尝试都会在某处导致编译器错误。

【讨论】:

  • 时不时地,我会再次进行这种推理。它总是让我头疼。
  • @Michael:唷——那么,我并不孤单。我的也是!
【解决方案3】:

请注意,标准未定义任何抛弃 const 的尝试。从标准的 7.1.5.1 开始:

除了声明的任何类成员 mutable 可以修改,任何 尝试修改 const 对象 在它的一生中 导致未定义的行为。

在使用这个例子之后:

const int* ciq = new const int (3);     //  initialized as required
int* iq = const_cast<int*>(ciq);        //  cast required
*iq = 4;                                //  undefined: modifies a  const  object

简而言之,使用标准 C++ 无法实现您想要做的事情。

进一步当编译器遇到类似声明时

const int a = 3; // I promisse i won't change a

可以随意将任何出现的“a”替换为 3(实际上与 #define a 3 做同样的事情)

【讨论】:

    【解决方案4】:

    回到过去,我们古程序员使用 FORTRAN。 FORTRAN 通过引用传递它的所有参数,并且没有进行任何类型检查。这意味着即使是字面常量也很容易意外更改值。您可以将“3”传递给 SUBROUTINE,它会返回更改,因此从那时起,每次您的代码有“3”时,它实际上都会像不同的值一样。让我告诉你,这些是很难找到和修复的错误。

    【讨论】:

    • “3”的事情让我很开心 :)
    【解决方案5】:

    你试过了吗?

    ptr = const_cast<int *>(ptr_to_a);
    

    这应该有助于它编译,但由于演员阵容,这并不是偶然的。

    【讨论】:

    • 是的,我知道,但我认为没有那个
    • 看起来你会得到正确的答案,但如果没有 const_cast 的解决方案出现,我会更长一点。我真的相信它确实如此
    • 不会导致请求的行为。
    【解决方案6】:

    在 C++ 中,使用 Microsoft Visual Studio-2008

    const int a = 3;    /* I promisse i won't change a */
    int * ptr1  = const_cast<int*> (&a);
    *ptr1 = 5;  /* I'm a liar, a is now 5 . It's not okay. */
    cout << "a = " << a << "\n"; /* prints 3 */
    int arr1[a]; /* arr1 is an array of 3 ints */
    
    int temp = 2;
    /* or, const volatile int temp = 2; */
    const int b = temp + 1; /* I promisse i won't change b */
    int * ptr2  = const_cast<int*> (&b);
    *ptr2 = 5; /* I'm a liar, b is now 5 . It's okay. */
    cout << "b = " << b << "\n"; /* prints 5 */
    //int arr2[b]; /* Compilation error */
    

    在C语言中,一个const变量可以通过它的指针来修改;然而,这是未定义的行为。 const 变量永远不能用作数组声明中的长度。

    在C++中,如果一个const变量用纯常量表达式初始化,那么即使尝试修改,也不能通过其指针修改其值,否则可以通过其指针修改const变量。

    纯整数 const 变量可以用作数组声明中的长度,如果其值大于 0。

    纯常量表达式由以下操作数组成。

    1. 数字文字(常量)例如2、10.53

    2. #define 指令定义的符号常量

    3. 枚举常量

    4. 纯 const 变量,即本身使用纯常量表达式初始化的 const 变量。

    5. 不允许使用非常量变量或易失变量。

    【讨论】:

      【解决方案7】:

      你可能想使用 const_cast:

      int *ptr = const_cast<int*>(ptr_to_a);
      

      我不是 100% 确定这会起作用,但我对 C/C++ 有点生疏 :-)

      一些关于 const_cast 的阅读:http://msdn.microsoft.com/en-us/library/bz6at95h(VS.80).aspx

      【讨论】:

        【解决方案8】:
        const int foo = 42;
        const int *pfoo = &foo;
        const void *t = pfoo;
        void *s = &t; // pointer to pointer to int
        int **z = (int **)s; // pointer to int
        **z = 0;
        

        【讨论】:

        • 你没有编译优化,是吗?
        • 虽然使用 VS2005,但它在我身上运行良好 :( 必须等待,然后才能接触 gcc。
        【解决方案9】:

        您正在查看的文章可能一直在谈论两者之间的区别

        const int *pciCantChangeTarget;
        const int ci = 37;
        pciCantChangeTarget = &ci; // works fine
        *pciCantChangeTarget = 3; // compile error
        

        int nFirst = 1;
        int const *cpiCantChangePointerValue = &nFirst;
        int nSecond = 968;
        
        *pciCantChangePointerValue = 402; // works
        cpiCantChangePointerValue = &ci; // compile error
        

        我记得——我这里除了 Java 工具外什么都没有,所以无法测试 :)

        【讨论】:

          【解决方案10】:

          其中一些答案指出编译器可以优化掉变量“a”,因为它被声明为const。如果您真的希望能够更改a 的值,那么您需要将其标记为volatile

            const volatile int a = 3; // I promise i won't change a
            int *ptr = (int *)&a;
            (*ptr) = 5; // I'm a liar, a is now 5
          

          当然,将某事声明为const volatile 应该真正说明这是多么愚蠢。

          【讨论】:

          • 我不认为这个词(“易失性”)意味着你认为它的意思。它与您是否可以更改值无关,而是确保对源代码的读取或写入在可执行文件中进行读取或写入,一对一。换句话说,没有缓存。
          • 这就是我的意思。编译器将看到 const 并优化该值。而如果你声明它是 volatile 的,它就不能像你说的那样“不缓存”。
          【解决方案11】:
          #include<iostream>
          int main( void )
          {
             int i = 3;    
             const int *pi = &i;
             int *pj = (int*)&i;
              *pj = 4;
          
             getchar(); 
             return 0;  
          }
          

          【讨论】:

            【解决方案12】:

            我正在研究如何在 const 之间进行转换,我发现这个 http://www.possibility.com/Cpp/const.html 也许它对某人有用。 :)

            【讨论】:

              【解决方案13】:

              我已经测试了下面的代码,它成功地改变了常量成员变量。

              #include <iostream>
              
              class A
              {
                  private:
                      int * pc1;  // These must stay on the top of the constant member variables.
                      int * pc2;  // Because, they must be initialized first
                      int * pc3;  // in the constructor initialization list.
                  public:
                      A() : c1(0), c2(0), c3(0), v1(0), v2(0), v3(0) {}
                      A(const A & other)
                          :   pc1 (const_cast<int*>(&other.c1)),
                              pc2 (const_cast<int*>(&other.c2)),
                              pc3 (const_cast<int*>(&other.c3)),
                              c1  (*pc1),
                              c2  (*pc2),
                              c3  (*pc3),
                              v1  (other.v1),
                              v2  (other.v2),
                              v3  (other.v3)
                      {
                      }
                      A(int c11, int c22, int c33, int v11, int v22, int v33) : c1(c11), c2(c22), c3(c33), v1(v11), v2(v22), v3(v33)
                      {
                      }
                      const A & operator=(const A & Rhs)
                      {
                          pc1     =  const_cast<int*>(&c1);
                          pc2     =  const_cast<int*>(&c2),
                          pc3     =  const_cast<int*>(&c3),
                          *pc1    = *const_cast<int*>(&Rhs.c1);
                          *pc2    = *const_cast<int*>(&Rhs.c2);
                          *pc3    = *const_cast<int*>(&Rhs.c3);
                          v1      = Rhs.v1;
                          v2      = Rhs.v2;
                          v3      = Rhs.v3;
                          return *this;
                      }
                      const int c1;
                      const int c2;
                      const int c3;
                      int v1;
                      int v2;
                      int v3;
              };
              
              std::wostream & operator<<(std::wostream & os, const A & a)
              {
                  os << a.c1 << '\t' << a.c2 << '\t' << a.c3 << '\t' << a.v1 << '\t' << a.v2 << '\t' << a.v3 << std::endl;
                  return os;
              }
              
              int wmain(int argc, wchar_t *argv[], wchar_t *envp[])
              {
                  A ObjA(10, 20, 30, 11, 22, 33);
                  A ObjB(40, 50, 60, 44, 55, 66);
                  A ObjC(70, 80, 90, 77, 88, 99);
                  A ObjD(ObjA);
                  ObjB = ObjC;
                  std::wcout << ObjA << ObjB << ObjC << ObjD;
              
                  system("pause");
                  return 0;
              }
              

              控制台输出为:

              10      20      30      11      22      33
              70      80      90      77      88      99
              70      80      90      77      88      99
              10      20      30      11      22      33
              Press any key to continue . . .
              

              这里的问题是,你必须定义与你拥有的常量成员变量一样多的指针。

              【讨论】:

                【解决方案14】:

                这将产生运行时错误。因为 intstatic。未处理的异常。访问冲突写入位置0x00035834。

                void main(void)
                {
                    static const int x = 5;
                    int *p = (int *)x;
                    *p = 99;                //here it will trigger the fault at run time
                }
                

                【讨论】:

                  【解决方案15】:

                  我们可以通过以下代码更改 const 变量值:

                  const int x=5; 
                  
                  printf("\nValue of x=%d",x);
                  
                  *(int *)&x=7;
                  
                  printf("\nNew value of x=%d",x);
                  

                  【讨论】:

                  • 当它打印New value of x=5时你会做什么?
                  【解决方案16】:
                  #include<stdio.h>
                  #include<stdlib.h>
                  
                  int main(void) {
                      const int a = 1; //a is constant
                      fprintf(stdout,"%d\n",a);//prints 1
                      int* a_ptr = &a;
                      *a_ptr = 4;//memory leak in c(value of a changed)
                      fprintf(stdout,"%d",a);//prints 4
                  return 0;
                  }
                  

                  【讨论】:

                    【解决方案17】:

                    最终解决方案:它将const变量更改一个值;

                    cont int a = 10;
                    *(int*)&a= 5; // now a prints 5
                    // works fine.
                    

                    【讨论】:

                      【解决方案18】:

                      您缺少的步骤是您不需要 int* 指针。行:

                      const int *ptr_to_a = &a; // I still promiss i won't change a;
                      

                      实际上是说你不会改变ptr_to_a,而不是a。因此,如果您将代码更改为如下所示:

                      const int a = 3; // I promise I won't change a
                      const int *ptr_to_a = &a; // I promise I won't change ptr_to_a, not a.
                      
                      (*ptr_to_a) = 5; // a is now 5
                      

                      a 现在是 5。您可以通过 ptr_to_a 更改 a 而不会发出任何警告。

                      编辑:

                      以上说法不正确。事实证明,我将类似的技巧与 shared_ptr 混淆了,您可以在其中访问原始指针并修改内部数据值而不会触发任何警告。那就是:

                      #include <iostream>
                      #include <boost/shared_ptr.hpp>
                      
                      int main()
                      {
                          const boost::shared_ptr<int>* a = new boost::shared_ptr<int>(new int(3));
                          *(a->get()) = 5;
                          std::cout << "A is: " << *(a->get()) << std::endl;
                      
                          return 0;
                      }
                      

                      将产生 5 个。

                      【讨论】:

                      • 肯定不能编译“错误:只读位置的分配”
                      • 不,ptr_to_a 是一个指向 const int 的指针,这意味着你保证不会改变 a。
                      • 是的 - 我知道我以前遇到过这个技巧,但这是一些变化。我正在尝试挖掘代码并找出我所看到的变体。
                      • 提升的东西也不是技巧。 const shared_ptr&lt;int&gt; a(new int(3)); *a = 5; 也可以。存储的指针本身不是常量。请尝试修改 shared_ptr&lt;const int&gt;
                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2016-01-20
                      • 1970-01-01
                      相关资源
                      最近更新 更多