在C ++中增加常量

时间:2014-09-30 19:53:53

标签: 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;
}

输出:

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

3 个答案:

答案 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仍然可以指向下一个项目,只有当我们考虑在内存中创建的堆栈时才可能。