【问题标题】:A pointer to const int in c++c++ 中指向 const int 的指针
【发布时间】:2019-11-24 17:30:39
【问题描述】:

在 c++ 中我有下一个代码

int main() {
    int i = 1;
    cout<<"i = "<<i<<endl; //prints "i = 1"

    int *iPtr = &i;
    cout<<"*iPtr = "<<*iPtr<<endl; //prints "*iPtr = 1"

    (*iPtr) = 12; //changing value through pointer

    cout<<"i = "<<i<<endl; //prints "i = 12"
    cout<<"*iPtr = "<<*iPtr<<endl; //prints "*iPtr = 12"

    system("pause");
    return 0;
}

现在与 常量整数 i

相同的代码
int main() {
    const int i = 1;
    cout<<"i = "<<i<<endl; //prints "i = 1"

    int *iPtr = (int*)&i; //here I am usint a type conversion
    cout<<"*iPtr = "<<*iPtr<<endl; //prints "*iPtr = 1"

    (*iPtr) = 12; //changing value through pointer

    cout<<"i = "<<i<<endl; //prints "i = 1"
    cout<<"*iPtr = "<<*iPtr<<endl; //prints "*iPtr = 12"

    system("pause");
    return 0;
}

如你所见,在第二种情况下,对于常量整数,*iPtr 和 const i 有两个不同的值,但指针 *iPtr 指向常量 i。 请告诉我第二种情况会发生什么,为什么?

【问题讨论】:

  • 未定义的行为。
  • 不要使用 C 风格的转换,你可以很容易地避免遇到这种未定义的行为。 static_cast 不会编译,你必须停下来思考“为什么”?
  • C++ 中有很多东西会让你更难用锤子击中自己的拇指,但如果你收紧并瞄准你的拇指......你可能会击中你的拇指。
  • 如果你对编译器撒谎((int*)&amp;i),编译器会做出意想不到的事情。
  • 如果您想知道为什么在更改原始值后会打印原始值,答案可能是编译器将 i 的任何使用替换为其值,即 12。由于它是一个常量,并且不允许任何有效代码对其进行修改,因此编译器的简化是有效的行为。无论如何,如果仍然不清楚,您的代码已损坏并导致所谓的“未定义行为”(搜索该术语!)。

标签: c++ pointers constants


【解决方案1】:

您的第二个代码有未定义的行为。您不能通过指向非常量的指针更改 const 数据。您很幸运,您的代码在尝试修改只读值时没有直接崩溃。

无论如何,您看到的结果是因为编译器知道 iconst 并且具有在编译时已知的值。因此编译器能够优化 icout 语句中并直接使用1。这就是为什么在打印 i 时看到 1 而在打印 *iPtr 时看到 12

【讨论】:

  • "你很幸运,你的代码没有简单地崩溃" 就我个人而言,我认为崩溃是最幸运的 UB 表现形式。
  • 我会省略有关“预期崩溃”的讨论。我不会:这种“将事物存储在只读存储器中”的概念对于这种情况来说是完全过时的;该值更有可能只是“内联”到程序指令中,而不是存储在任何地方。是的,该变量后来被 odr 使用,但程序具有未定义的行为,因此这意味着几乎没有。让我们只关注合同违规行为,忽略我们不应该教人们首先做出的低级假设!
  • 您也可以将 i 声明为 const volatile int,以避免缓存值。
  • .... 像这样。不,瓦哈格,这是一个假设。你不能通过做这样的聪明技巧来“避免”UB。您不是在对计算机进行编程:您正在使用抽象来描述程序,并且它必须是明确定义的,否则所有赌注都没有。时期。句号。
  • @LightnessRaceswithMonica 它当然仍然是具有 volatile const 的 UB,但它确实会强制在每次读取访问时重新读取,因此它似乎可以工作。这有几个主要问题。 UB 但“有效”的代码是一个潜在的未来错误,很难找到。第二个问题是代码禁用了一些优化。
【解决方案2】:

您正在尝试删除变量的 const 限定符。 在 C++ 中,您应该使用 const_cast 来执行此操作。

但是,const_cast 只能在某些精确的情况下使用:constness 只能从指向已在顶层声明为非常量的数据的指针/引用中删除,否则编译器可能会优化变量并通过指针/引用会导致未定义的行为。

例如,这是不合法的:

const int i = 1;
const int *iPtr = &i;
int *iSuperPtr = const_cast<int*>(iPtr);
*iSuperPtr = 2; // Invalid : i is first declared const !!

但这是完全合法的:

void modifyConstIntPtr(const int *iPtr) {
    int *iSuperPtr = const_cast<int*>(iPtr);
    *iSuperPtr = 2; // Valid : i is first declared non-const !!
}

void modifyConstIntRef(const int &iRef) {
    int &iSuperRef = const_cast<int&>(iRef);
    iSuperRef = 3; // Valid : i is first declared non-const !!
}
int main() {
    int i = 1;
    modifyConstIntPtr(&i);
    std::cout << i << std::endl;
    modifyConstIntRef(i);
    std::cout << i << std::endl;
}

C++ 的这个方面在这里有详细的介绍:https://stackoverflow.com/a/357607/3412316)

【讨论】:

  • 这与优化无关。
  • const_cast 永远不会无效(只要您只删除顶级const;否则您的程序将无法编译)-它正在修改可能不好的结果
猜你喜欢
  • 2015-09-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-10
  • 1970-01-01
相关资源
最近更新 更多