【问题标题】:Undefined behaviour of constconst 的未定义行为
【发布时间】:2022-01-16 20:45:36
【问题描述】:

我从没想过我会问这个问题,但我不知道为什么会这样。

const int a = 3; 
int *ptr;
ptr = (int*)( &a );
printf( "A=%d\n", &a );
*ptr = 5; 
printf( "A=%d\n", ptr );
printf( "A=%d\n", a );
printf( "A=%d\n", *ptr );

输出

A=6945404
A=6945404
A=3
A=5

怎么会这样?一个内存位置如何保存两个不同的值?我四处搜索,我发现的只是未定义的行为是未定义的。那么这没有任何意义。必须有解释。

编辑

我明白了,Marks 的答案很有意义,但我仍然想知道 const 是否被添加到语言中,以便用户不会无意中更改值。我知道旧的编译器允许你这样做,但我在 VS 2012 上试过这个,我得到了同样的行为。然后又像haccks 所说的那样,一个内存位置不能保存看起来像的两个值,那么第二个值存储在哪里?

【问题讨论】:

  • 对不起,请参阅编辑
  • 未定义的行为意味着任何事情都可能发生。代码可能完全按预期运行,可能会崩溃,或者可能会偷走你的女朋友。
  • 您的编译器中是否配置了任何优化级别?
  • 是的,我知道,我知道发生了什么,但是 const 被引入到语言中,只是为了让用户不会无意中更改值,这就是为什么我感到不安,为什么它允许我
  • 您正在将const-指针投射到不合格的指针。所有的赌注都取消了。就如此容易。如果您不知道您的目标是什么,请不要扔菜刀。

标签: c++ c


【解决方案1】:

优化器可以确定a 是一个常量值,并将对它的任何引用替换为文字3。这解释了你所看到的,尽管不能保证这就是实际发生的事情。您需要为此研究生成的程序集输出。

【讨论】:

  • 我的问题再次是一个内存位置如何保存两个不同的值。堆栈中到底有什么?如果我动态创建它并且不删除它怎么办?那里会留下什么? 3 还是 5?
  • Hamza,你错过了 Mark 的观点。当您编写const int a=3; 时,您为编译器提供了将代码中对a 的每个子序列引用解释为含义3 而不是变量a 的选项。
  • @Hamza 我有点不具体。我的意思是说,对printf("%d",a) 的调用是替换文字值的调用。所有其他语句都使用唯一的内存位置。正如我所说,至少我猜这是在没有看到程序集输出的情况下发生的。
  • @Hamza 编译器不会给你一个错误,因为你故意对它撒谎。您在创建指针时抛弃了const 属性,因此它必须假设您知道自己在做什么,并且一开始它并不是真正的const。有时我不得不在调用一个没有使用const 指针的函数时这样做,即使我知道它不会改变值。不过,当我这样做时,我不得不捂住鼻子。
  • @Hamza 在 C++ 中防止自己在不安全且故意覆盖类型系统时不被认为是编译器的问题。此类检查可能很昂贵,无论是额外的运行时检查还是阻塞优化(例如 a 3 替换)。类型系统可以帮助你,但它不会强迫你玩得很好。
【解决方案2】:

通过非常量指针修改 const 变量会导致未定义的行为。最有可能的是优化器正在替换这一行中的原始值:

printf( "A=%d\n", a );

查看反汇编以验证这一点。

C 标准第 6.7.3 条第 6 段 [ISO/IEC 9899:2011] 规定: 如果尝试通过使用具有非 const 限定类型的左值来修改使用 const 限定类型定义的对象,则行为未定义。

【讨论】:

    【解决方案3】:

    事实上,您的程序调用未定义的行为有两个原因:
    1.您打印的地址使用错误的说明符%d。正确的说明符是%p
    2.您正在使用const 说明符修改变量。

    如果行为未定义,那么任何事情都可能发生。您可能会得到预期或意外的结果。
    标准对此进行了说明;

    3.4.3 未定义行为

    行为,在使用不可移植或错误的程序结构或错误数据时, 本国际标准对此没有要求

    【讨论】:

    • 即使我使用%p 也没关系,它有相同的地址。再说一遍,一个内存位置如何保存两个不同的值。
    • 不能。由于行为是未定义的,它似乎实际上持有两个值。
    • 我认为一切都是二进制的,不管它是否成立。那么它不能这样存储第二个值在哪里.. 深思
    • 我不确定该值的存储位置。
    • 如果我成为编译器,那么它可能是:P
    【解决方案4】:

    问题在于 ptr 的类型是“指向 int 的指针”而不是“指向 const int 的指针”。 然后,您将“a”(一个 const int)的地址转换为“指向 int 的指针”类型,并将该地址存储在 ptr 中。这样做的效果是你抛弃了 const 变量的 const 特性。

    这会导致未定义的行为,因此您的结果可能因编译器而异。

    编译器可以将“a”存储在程序 ROM 中,因为它知道“a”是一个永远无法更改的 const 值。当您对编译器撒谎并抛弃“a”的常量以便您可以通过ptr对其进行修改时,ptr实际修改“a”的值可能是无效的,因为该数据可能存储在程序ROM中.这次编译器没有给你崩溃,而是决定将 ptr 指向不同的位置,这次具有不同的值。但由于这种行为未定义,任何事情都可能发生。

    【讨论】:

    • 兄弟,我一直都这么认为,我使用的是 VS 2012,所以我很困惑,它应该给我一个错误,我正在更改常量值
    • 它是否给你一个错误取决于编译器供应商,因为标准未定义这种情况。他们可以在这种情况下做任何事情,并且仍然符合标准。
    • 编译器很难确定它应该给出一个错误,因为这两个操作都不是非法的。您可以抛弃 const (在本例中为非 const 指针),并且可以通过生成的非 const 指针修改值。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-06-20
    • 1970-01-01
    • 2022-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多