我写了一个像这样的c ++代码:
#include <iostream>
using namespace std;
int main() {
int i = 2;
int i2 = 0;
void *pi = &i - 1;
cout << "by cout - the value of *pi is: " << *(int*)pi << endl;
printf("by printf - the value of *pi is: %d\n", *(int*)pi);
printf("the address of pi is: %p\n", pi);
printf("the address of i2 is: %p\n", (void*)&i2);
printf("the value of i2 is: %d\n", i2);
return 0;
}
,输出为:
by cout - the value of *pi is: 0
by printf - the value of *pi is: 0
the address of pi is: 0029fe94
the address of i2 is: 0029fe94
the value of i2 is: 0
现在,如果我删除将打印地址的语句。
#include <iostream>
using namespace std;
int main() {
int i = 2;
int i2 = 0;
void *pi = &i - 1;
cout << "by cout - the value of *pi is: " << *(int*)pi << endl;
printf("by printf - the value of *pi is: %d\n", *(int*)pi);
// printf("the address of pi is: %p\n", pi);
// printf("the address of i2 is: %p\n", (void*)&i2);
printf("the value of i2 is: %d\n", i2);
return 0;
}
现在的输出是:
by cout - the value of *pi is: 2004212408
by printf - the value of *pi is: 2004212408
the value of i2 is: 0
注意价值完全不同。
更新: 如果我在打印后添加一些作业:
#include <iostream>
using namespace std;
int main() {
int i = 2;
int i2 = 0;
void *pi = &i - 1;
cout << "by cout - the value of *pi is: " << *(int*)pi << endl;
printf("by printf - the value of *pi is: %d\n", *(int*)pi);
// printf("the address of pi is: %p\n", pi);
// printf("the address of i2 is: %p\n", (void*)&i2);
pi = &i2;
printf("the value of i2 is: %d\n", i2);
return 0;
}
输出再次正常:
by cout - the value of *pi is: 0
by printf - the value of *pi is: 0
the value of i2 is: 0
使用&#34; g ++ -std = c ++ 11 -pedantic -Wall&#34;要编译,版本是4.9.2。
为什么会发生这种情况?
答案 0 :(得分:8)
由于pi
可能不是有效的指针值,因此访问&i - 1
是未定义的行为。
在标准(来自C ++标准)中,在§5.3.1/ 3中我们有:
[...]出于指针算法(5.7)和比较(5.9,5.10)的目的,不是以这种方式获取地址的数组元素的对象被认为属于具有一个元素的数组。输入T。
引导我们进入§5.7/ 4:
当向指针添加或从指针中减去具有整数类型的表达式时,结果具有指针操作数的类型。如果指针操作数指向数组对象的元素,并且数组足够大,则结果指向偏离原始元素的元素,使得结果元素和原始数组元素的下标的差异等于整数表达式。 [...]如果指针操作数和结果都指向同一个数组对象的元素,或者指向数组对象的最后一个元素,则评估不应产生溢出;否则,行为未定义。
答案 1 :(得分:4)
是的,正如已经说过的那样,你的程序有不确定的行为。你不能用这样的指针来解决这个问题,并期待一致的行为。
为什么呢?因为编译器是复杂的生物,会做各种奇怪的魔法,会破坏你对内存布局的期望。
在这种情况下,您只是假设 i2
将紧接在i
之前的内存中。这种假设毫无根据。虽然在您的工作示例中发生就是这种情况,但当您的程序不强迫i2
占用内存时,请将其地址(&i2
放在您的& #34; print statement&#34;)变量显然完全优化。它并不存在于内存中!
所以难怪试图猜测它的内存位置并打印出所述内存位置下面的值并不像你期望的那样工作。
C ++是抽象。它是故意设计的,知道这种东西真的是编译器的选择,无法提前预测。这就是为什么C ++说你的代码有未定义的行为的原因:因为它可能看起来像你想做的那样,或者它可能会使猫皮肤传送到你的汽车引导装置,或者它可能会将您辛苦赚来的所有积蓄转入我的银行账户;如果你想编写标准代码,只是避免未定义的行为,假设只有 标准的保证,那么所有这些不确定性就会在一连串的猫薄荷中消失。