我写了2个程序。请仔细阅读这两个程序并帮助我理解为什么变量'i'和'* ptr'给出不同的值。
//Program I:
//Assumption: Address of i = 100, address of ptr = 500
int i = 5;
int *ptr = (int *) &i;
*ptr = 99;
cout<<i; // 99
cout<<&i;// 100
cout<<ptr; // 100
cout<<*ptr; // 99
cout<<&ptr; // 500
//END_Program_I===============
//Program II:
//Assumption: Address of i = 100, address of ptr = 500
const int i = 5;
int *ptr = (int *) &i;
*ptr = 99;
cout<<i; // 5
cout<<&i;// 100
cout<<ptr; // 100
cout<<*ptr; // 99
cout<<&ptr; // 500
//END_PROGRAM_II===============
混淆是:为什么变量我仍然是5,即使* ptr == 99?
答案 0 :(得分:6)
在以下三行中,您正在修改常量:
const int i = 5;
int *ptr = (int *) &i;
*ptr = 99;
这是未定义的行为。任何事情都可能发生。所以不要这样做。
至于在这种特殊情况下发生的事情:
由于i
是const
,编译器认为它不会改变。因此,它只是简单地将5
内联到每个使用它的地方。这就是为什么打印i
会显示5
的原始值。
答案 1 :(得分:5)
所有答案都可能会讨论“未定义的行为”,因为你正在尝试修改常量的逻辑无意义。
虽然这在技术上是完美的,但是让我给你一些关于为什么会发生这种情况的提示(关于“如何”,请参阅Mysticial回答)。
之所以发生这种情况,是因为C ++是设计“不完美指定的语言”。 “不完美”包含许多贯穿语言规范的“未定义行为”。
事实上,语言设计师故意选择 - 在某些情况下 - 而不是说“如果你这样做,会给你那个”,(可能是:你有这个代码,或者你有这个错误),他更喜欢说“我们没有定义会发生什么”。
这使编译器制造商可以自由决定做什么。并且由于有许多编译器在许多平台上工作,可能是一个最佳解决方案,不一定是另一个的最佳解决方案(可能依赖于具有不同指令集的机器),因此您(作为程序员)被留下在戏剧性的情况下,你永远不会知道会发生什么,即使你测试它,你也不能相信测试的结果,因为在另一种情况下(用不同的编译器编译相同的代码或只是不同的版本) ,或者对于不同的平台,它会有所不同。
这里的“坏”事情是编译器应该在遇到未定义的行为时发出警告(强制const 应该被警告为潜在的错误,< em>特别是如果编译器执行const-inlining otimizations ,因为如果允许更改const是无意义的话,如果你指定了正确的标志(可能是-W4或 - )根据您所拥有的编译器,可以使用wall或-pedantic或类似的内容。
特别是
行int *ptr = (int *) &i;
应该发出如下警告:
warning: removing cv-qualifier from &i.
因此,如果您将程序更正为
const int *ptr = (const int *) &i;
为了满足回收,你将在
处得到一个错误*ptr = 99;
如
error: *ptr is const
从而使问题变得明显。
故事的道德:
从法律的角度来看,您编写了错误的代码,因为它是-by语言定义 - 依赖于未定义的行为。
从道德的角度来看:编译器保持了不公平的行为:在接受后执行const-inlining(用cout << i
替换cout << 5
) > (int*)&i
是一种自我矛盾,至少应该警告不连贯的行为。
如果它想做一件事就不能接受另一件事,反之亦然。
因此,检查是否有可以设置警告的标志,如果不是,向编译器制造商报告其不公平性:它没有警告其自身的矛盾。
答案 2 :(得分:2)
const int i = 5;
意味着变量i
是const
并且它不能/不应该被更改,它是Imuttable并且通过指针更改它会导致未定义行为。
未定义的行为意味着程序格式错误,任何行为都是可能的。您的程序似乎可以按预期工作,或者甚至不会崩溃。所有安全的赌注都已关闭。
记住规则:
修改const
变量是未定义的行为。不要这样做。
答案 3 :(得分:1)
您正在尝试通过未定义的指针修改常量。这意味着任何意外情况都会发生,从正确的输出到错误的输出,再到程序崩溃。