有人可以向我解释为什么这段代码有效吗?我觉得编译器不应该允许我做我做的事情(移动一个指针指向一个const int),或者我至少会期望编译器警告或段错误。改变常数值的想法似乎是错误的。
代码:
#include <iostream>
using namespace std;
struct test_struct {
int i;
const int j;
};
int main() {
cout << "Create a struct with int i = 100 and const int j = 101." << endl;
test_struct test{100, 101};
cout << test.i << endl;
cout << test.j << endl;
cout << "Create pointer p and point it to int i." << endl;
int* p1 = &test.i;
cout << *p1 << endl;
cout << "Increment pointer p, which should now be pointing at const int j." << endl;
p1++;
cout << *p1 << endl;
cout << "Dereference p and increment it." << endl;
(*p1)++;
cout << *p1 << endl;
cout << test.j << endl;
}
输出:
Create a struct with int i = 100 and const int j = 101.
100
101
Create pointer p and point it to int i.
100
Increment pointer p, which should now be pointing at const int j.
101
Dereference p and increment it.
102
102
答案 0 :(得分:18)
你的程序以两种方式调用undefined behavior,这意味着程序的行为是不可预测的,即使看似正常的行为也是可能的。
首先,虽然我们可以将结构的各个元素视为数组,但是一旦递增指针就不再有效取消引用它,它甚至不必指向它可能指向的下一个元素。填充。
其次,尝试在未定义的行为中改变const。 draft C++ standard部分7.1.6.1
cv-qualifiers 段 4 表示:
[...]任何在生命周期内修改const对象的尝试(3.8)都会导致未定义的行为。
我们可以看到,为了指针算法的目的,非数组变量被视为一个元素的数组,来自5.7
添加运算符部分,其中包含:
出于这些运算符的目的,指向非阵列对象的指针 行为与指向数组的第一个元素的指针相同 长度为1,对象的类型为元素类型。
并且从一个数组的末尾取消引用一个是未定义的行为,来自同一部分:
添加或减去具有整数类型的表达式时 从指针开始,结果具有指针操作数的类型。 [...] 如果指针操作数和结果都指向了元素 相同的数组对象,或者超过数组对象的最后一个元素, 评估不得产生溢出;否则,行为 未定义。
我们可以从5.3.1
一元运算符部分进一步了解:
一元*运算符执行间接:它所表达的表达式 应用应该是指向对象类型的指针,或指向a的指针 函数类型,结果是左值引用对象或 功能
当我们取消引用我们期望的指针和 object 时,一旦我们结束了,我们就不能保证这些指针。
The GNU C++ Library有一个更容易理解的说明(强调我的):
您只能取消引用指向数组的指针。如果你的 数组指针指向数组外部 - 甚至只是一个数组 结束 - 你取消引用它,糟糕的事情发生。
答案 1 :(得分:2)
(对于Visual Studio 2010,这个答案是正确的 - 不确定其他编译器。)
以下是允许这样做的原因:
const
修饰符是编译器的指令,用于防止用户编辑该声明的变量。使用该变量时,编译器将阻止您对其进行更改,并要求您将const
修饰符添加到与该特定变量关联的指针。
但是,所有像其他变量值一样,它驻留在内存中,并且编译器不会专门阻止访问或编辑该内存。如果您选择颠覆编译器的指令,就可以像使用指针的任何其他内存地址一样访问和修改它,就像在代码中一样。
如果您希望阻止程序访问内存中的区域,可以参考Windows的以下内存保护常量:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa366786(v=vs.85).aspx
答案 2 :(得分:-1)
结构中的数据项存储在内存中的堆栈中,因此当您创建指针并使其指向第一个项时,存储的地址是堆栈指针的位置。当您将其递增时,堆栈指针指针递增到堆栈上的下一个位置,即第二个数据项。 因此,这可能是可能的原因。因为否则它应该给出错误,而且我们也不能像数组一样对待结构。 Bt仍然可以指向下一个项目,只有当我们考虑在内存中创建的堆栈时才可能。