在C++中增加常量

rol*_*ack 5 c++ pointers const undefined-behavior

有人可以向我解释为什么这段代码有效吗?我觉得编译器不应该允许我做我做的事情(移动一个指针指向一个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;
}
Run Code Online (Sandbox Code Playgroud)

输出:

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
Run Code Online (Sandbox Code Playgroud)

Sha*_*our 18

程序以两种方式调用未定义的行为,这意味着程序的行为是不可预测的,即使看似正常的行为也是可能的.

首先,虽然我们可以将结构的各个元素视为数组,但是一旦递增指针就不再有效取消引用它,它甚至不必指向它很可能指向填充的下一个元素.

其次,尝试在未定义的行为中改变const.该草案C++标准7.1.6.1 的cv修饰符4,其表示:

[...]任何在其生命周期内修改const对象的尝试(3.8)都会导致未定义的行为.

我们可以看到,为了指针运算的目的,非数组变量被视为一个元素的数组,来自5.7 Additive运算符,它说:

出于这些运算符的目的,指向非阵列对象的指针与指向长度为1的数组的第一个元素的指针的行为相同,其中对象的类型为其元素类型.

此外,从同一部分中取消引用一个数组末尾的未定义行为:

当向指针添加或从指针中减去具有整数类型的表达式时,结果具有指针操作数的类型.[...]如果指针操作数和结果都指向同一个数组对象的元素,或者指向数组对象的最后一个元素,则评估不应产生溢出; 否则,行为未定义.

我们可以从一5.3.1 元运算符部分进一步看到:

一元*运算符执行间接:它所应用的表达式应该是指向对象类型的指针,或指向函数类型的指针,结果是引用对象或函数的左值

当我们取消引用我们期望的指针和我们不能保证的对象,一旦我们超过结束.

GNU的C++库有一个更简单的访问说明它说(重点煤矿):

您只能取消引用指向数组的指针.如果你的数组指针指向数组外部 - 甚至只是一个结束 - 并且你取消引用它,就会发生糟糕的事情.