2014-09-30 2 views
5

Может кто-нибудь объяснить мне, почему этот код работает? Я чувствую, что компилятор не должен позволять мне делать то, что я сделал (переместите указатель int, чтобы указать на const int), или, альтернативно, я бы ожидал, что вы увидите предупреждение компилятора или segfault. Идея изменить значение константы просто кажется неправильной.Увеличение константы в C++

Код:

#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

какой компилятор вы используете? – Creris

+0

Я использую компилятор g ++. – rolledback

+0

Лучшим заголовком будет: изменение константы в структуре путем увеличения указателя на неконстантный член. – Csq

ответ

18

программа Вы вызывает двумя способами, что означает поведение вашей программы непредсказуемо, даже, казалось бы, нормальное поведение возможно undefined behavior.

Во-первых, хотя мы можем обрабатывать отдельные элементы структуры как массивы после того, как вы увеличиваете указатель, он больше недействителен для разыменования его, он даже не должен указывать на следующий элемент, который он вполне мог бы указывать для заполнения.

Во-вторых, попытка изменить константу также в неопределенном поведении. В draft C++ standard раздел 7.1.6.1ЦВ-отборочные пункт который говорит:

[...] любая попытка изменить константный объект в течение всего срока службы (3.8) приводит к неопределенному поведению.

Мы можем видеть, для целей арифметики указателей переменный не-массив рассматриваются как массив из одного элемента, из секции 5.7аддитивных операторов, который говорит:

Для целей настоящего операторов, указатель на объект nonarray ведет себя так же, как указатель на первый элемент массива , длина которого совпадает с типом объекта в качестве его типа элемента.

и, кроме того dereferecing один за концом массива является неопределенное поведение, из того же раздела:

Когда выражение, которое имеет интегральный тип добавляется или вычитается из указателя, результат имеет тип операнда указателя. [...] Если оба операнда указателя и результат указывают на элементы одного объекта массива или один из последних элементов объекта массива , то оценка не должна приводить к переполнению; в противном случае поведение не определено.

мы можем еще увидеть из раздела 5.3.1унарные операторы который говорит:

Унарный * оператор выполняет косвенность: выражение, к которому он применяется должен быть указателем на тип объекта, или указатель на тип функции, и результат является именующего со ссылкой на объект или функций

, когда мы разыскиваем указатель, который мы ожидаем, и объект, который у нас не гарантируется, когда мы один за концом.

The GNU C++ Library имеет простой доступ к объяснение, которое говорит (внимание мое):

Вы можете только разыменования указателя, который указывает на массив. Если ваш указатель на массив указывает вне массива - даже на один конец конец - и вы разыгрываете его, Плохие вещи происходят.

+0

Какой проект? Было бы лучше добавить проектную версию для дальнейшего использования. – Csq

+0

Хорошо. Это имеет смысл, так как мой профессор сказал что-то очень похожее. Наверное, я больше удивляюсь, что в этом случае неопределенное поведение не похоже на segfault или p, указывающее на какое-то странное место в памяти. – rolledback

+0

@Csq добавил ссылку на проект, на который я ссылался. –

1

(Этот ответ является правильным для Visual Studio 2010 - не уверен, о других компиляторов.)

Вот причина, почему это разрешено:

Модификатор const является инструкцией к компилятор, чтобы запретить пользователю редактировать эту объявленную переменную. При работе с этой переменной компилятор помешает вам внести изменения в него и потребует добавить модификатор const указателям, связанным с этой конкретной переменной.

Однако, как и другие переменные, он находится в памяти, а компилятор не запрещает доступ к этой памяти или ее редактирование. Он может быть доступен и изменен так же, как и любой другой адрес памяти, используя указатель, если вы решите подорвать инструкции компилятора, как это было в вашем коде.

Если вы хотите, чтобы предотвратить доступ программ в область памяти, вы можете обратиться к следующим константам защиты памяти для Windows:

http://msdn.microsoft.com/en-us/library/windows/desktop/aa366786(v=vs.85).aspx

+0

_ «Итак, вот почему вы можете это сделать». _ Не остается ли UB UB? –

+0

Я должен указать, что только потому, что вы можете это сделать, это не значит, что вы должны или что это хорошая практика кодирования. Он попросил объяснить, почему это сработало. – user2076574

+0

Учитывая, что действительная практика «кодирования» _ самой удаленной возможностью, очевидно, ошибочна (не говоря уже смешно). –

-1

элементов данных в структуре хранится в стеке в памяти , поэтому, когда вы создаете указатель и указываете на первый элемент, адрес, который хранится, является позицией указателя стека. Когда u увеличивает его, указатель стека увеличивается до следующего места в стеке, т.е. второго элемента данных. Таким образом, это может быть возможной причиной. Потому что в противном случае она должна была дать ошибку, а также мы не можем рассматривать структуру, подобную массиву. Bt все еще способен указывать на следующий элемент, возможно, только если мы рассмотрим стек, созданный в памяти.

 Смежные вопросы

  • Нет связанных вопросов^_^