【问题标题】:Why does compiler allow you to "write" a const variable here?为什么编译器允许你在这里“写”一个 const 变量?
【发布时间】:2010-11-16 23:03:18
【问题描述】:

为什么你可以这样欺骗编译器:

const int a = 5;
*((int*)&a)=5;   // VC/armcc does not complain

当上面的“删节”相当于:

const int *ptr2const = &a;
int *ptr = ptr2const;      // as expected error is raised here
*ptr = 5;

【问题讨论】:

  • 您的代码的第二个版本与第一个版本不“等效”。 C 语言需要显式类型转换才能从指针类型中删除限定符;正确的编译器可以(并且应该)拒绝您编写的第二段代码。可悲的是,大多数人只是发出警告。

标签: c++ c pointers constants


【解决方案1】:

强制转换是你告诉编译器“我知道我在做什么”的方式,所以它不会抱怨。不幸的是,在这种情况下,您将调用未定义的行为。

【讨论】:

  • 或者,“不幸的是,在这种情况下,你不知道自己在做什么”;-)
【解决方案2】:

C 风格的强制转换允许您像示例中那样抛弃 constness。在 C++ 中,您通常会使用新的样式转换,例如 static_cast<>,它不允许您丢弃 constness。只有const_cast<> 允许您这样做。

【讨论】:

  • ..C 编译器不会阻止您写入 const 变量,并且 const(除了一些明显的情况)对您没有多大帮助:-D
  • @Artur:当然可以。当您尝试修改常量变量时​​,会出现编译器错误。没有比这更直接的了。它让你做的是显式转换类型,所以你可以整天做愚蠢的事情。但是不要误会这种愚蠢并说 C 编译器或 const 没有帮助;它在你身上。
  • @GMan - 我同意 - 我不是每天都写这样的代码,但今天我看到 void 指向 const volatile 的指针作为例程的参数并开始使用它,结果证明(没想到会这样)理论上你可以用该指针指向的内存区域做任何事情,并且添加 const 不会使编译器甚至警告你。这就是为什么我写道,除了一些明显的情况(如取消引用和赋值)之外,您的编译器不会尽力阻止您写入 const 区域/变量。我没想到编译器会“忽略”它。
  • @Artur:对。 Const 只是一个编译时类型注释,它不如许多其他语言中的类型注释有用和灵活。它或多或少没用,尽管很多人仍然将它用作一种代码文档。
【解决方案3】:

为了等价,第2个sn-p的第2行

int *ptr = ptr2const;      // as expected error is raised here

应该写成

int *ptr = (int *)ptr2const;

【讨论】:

    【解决方案4】:

    因为 C 放弃了很多类型安全性来获得很多速度。它不能阻止你做不正确的事情。它可能会尝试警告您您正在做不正确的事情,但如果这是您的目标,您可以始终绕过编译器。

    【讨论】:

      【解决方案5】:

      将您的常量转换为字符串,您可能会发现,虽然 编译器 会让您丢弃 const(尽管这可能是不可取的),但链接器可能会将常量字符串置于读取状态- 仅导致运行时崩溃的内存。

      【讨论】:

        【解决方案6】:

        C 风格的强制转换,例如 (int*) 等价于 C++ const_cast,因为它们具有抛弃 const 的能力,因此您可以通过使用它们来回避 const 正确性,尽管不推荐使用这种类型(可能导致未定义的行为)。

        int main()
        {
            const int x = 1;
            (int&)x = 2;
            std::cout << x << std::endl;
        }
        

        在我的系统上,上面将1 写入标准输出。您可能会遇到不同的行为。

        另一方面...

        void foo(const int& x)
        {
            (int&)x = 2;
        }
        
        int main()
        {
            int x = 1;
            foo(x);
            std::cout << x << std::endl;
        }
        

        这是为我写的2。不同之处在于foo 中使用的const 是作为类型限定符的const,而在第一个示例中的main 中它被用作存储类。要查看变量是否使用 const 作为存储类声明并不总是那么容易,因此最好不要依赖 const_cast 或 C 风格的强制转换来抛弃 const。

        在大多数情况下最好只使用static_cast,因为这会在编译时以编译错误的形式警告您任何可疑行为。

        【讨论】:

        • 它们不相等。此特定实例给出与const_cast 相同的结果。 C++ 对不同性质的强制转换使用不同的强制转换语法的原因正是为了避免意外调用这样的无效强制转换。
        【解决方案7】:

        这只是“有效”,因为变量是本地的,并且实现通常无法在本地(自动)变量上强制执行 const-ness(至少在变量的地址被占用时不会)。但就语言规范而言,这是未定义行为的范畴。

        如果您在全局/静态变量上尝试此操作,您会很快发现大多数实现 can 和 do 通过将变量放入 read- 来强制执行 const-只有可以在程序的多个实例之间共享的内存。

        【讨论】:

          猜你喜欢
          • 2015-09-06
          • 1970-01-01
          • 1970-01-01
          • 2011-03-05
          • 2021-11-01
          • 2022-01-19
          • 1970-01-01
          • 1970-01-01
          • 2020-04-15
          相关资源
          最近更新 更多