2016-06-29 3 views
0
struct Vec 
{ 
    double x, y, z; 
}; 

Vec vec; 
vec.x = 1.0; 
vec.y = 2.0; 
vec.z = 3.0; 
double res = (&vec.x)[2]; // (&vec.x)[2] should be equal to vec.z 

Значения переменной Реза должна быть равно 3. Но когда я включаю оптимизации на компилятор реорганизует инструкцию неправильно и разрешения содержит некоторый мусор. Некоторые возможные варианты переупорядочения:Компилятор инструкции переназначения

vec.x = 1.0; 
vec.y = 2.0; 
vec.z = 3.0; 
res = (&vec.x)[2]; // correct value 

vec.x = 1.0; 
vec.y = 2.0; 
res = (&vec.x)[2]; // incorrect value 
vec.z = 3.0; 

vec.x = 1.0; 
res = (&vec.x)[2]; // incorrect value 
vec.y = 2.0; 
vec.z = 3.0; 

Это ошибка в компиляторе? Или не разрешается доступ к элементам данных структуры, подобным этому?

EDIT:

Я просто понял, что предыдущий код на самом деле работает, извините за это. Но это не работает:

Vec vec; 
vec.x = 1.0; 
vec.y = 2.0; 
vec.z = 3.0; 
double res = (&vec.x)[i]; // (&vec.x)[i] should be equal to vec.z when i == 2 

Когда переменная я не знаю, во время компиляции, компилятор реорганизует инструкции неправильно.

+3

@BatCoder: Если это неопределенное поведение, не имеет значения, можете ли вы его воспроизвести или нет. –

+0

Хэйверс, что значит '(& vec.x) [2]', как вы это понимаете? – osgx

+0

(& vec.x) [2] получает адрес vec.x, добавляет 2 и разыскивает его. Он должен вернуть значение vec.z –

ответ

18

Вы вызываете неопределенное поведение. (&vec.x)[2] является equivilant к (&vec.x) + 2, и стандарт (§ [expr.add]/4) имеет следующие сказать о дополнение указателя (курсив мой):

Когда выражение, которое имеет целочисленный тип добавляется или вычитается от указателя, результат имеет тип операнда указателя. Если указатель операндов указывает на элемент объекта массива , и массива достаточно велика, то результат указывает на элемент смещения от исходного элемента таким образом, что разность индексов в результате и оригинальный элементы массива равны интегральному выражению. Иными словами, если выражение P указывает на i-й элемент объекта массива , выражения (P) + N (эквивалентно, N + (P)) и (P) -N (где N имеет значение n) указывают, соответственно, i + n-th и i- n-ые элементы массива, если они существуют. Более того, если выражение P указывает на последний элемент объекта массива, выражение (P) +1 указывает один за последним элементом объекта массива, и если выражение Q указывает один за последним элементом array объект, выражение (Q) -1 указывает на последний элемент массива объект. Если оба операнда указателя и результат указывают на элементы того же объекта массива или один за последним элементом массива объект, оценка не должна приводить к переполнению; в противном случае поведение не определено.

Смотрите также отметить 84 ссылки выше:

84) Объект, который не является элементом массива считается принадлежащей к массиву одноэлементной для этой цели; см. 5.3.1.

С vec.x не является массивом, он считается элементом одноэлементного массива. Таким образом, vec.x и vec.z не являются элементами одного и того же массива, и поведение не определено.

2

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

struct Vec 
{ 
    double elems[3]; 
    double& x; 
    double& y; 
    double& z; 

    Vec() 
    : x(elems[0]), y(elems[1]), z(elems[2]) 
    { 
    } 
}; 

Если вы не можете позволить себе возможный (но не обязательно) дополнительное пространство для каждого объекта, вы можете также объявить х , y, z в качестве функций, возвращающих ссылку.

0

я в конце концов нашел решение, которое предотвращает инструкции переназначения и не увеличивает размер структуры Vec:

struct Vec 
{ 
    union 
    { 
     struct 
     { 
      double x, y, z; 
     }; 
     double data[3]; 
    }; 
}; 

Vec vec; 
vec.x = 1.0; 
vec.y = 2.0; 
vec.z = 3.0; 
double res = vec.data[i]; 

Спасибо за ваши ответы.

+0

Is это гарантировало работу? или это специфическое поведение компилятора? Вы тестировали другие компиляторы? – will

+0

Это все еще UB, дважды так. – lorro

+0

sizeof (Vec) == 24. Союз использует одну и ту же память для x и данных [0], y и данных [1], z и данных [2]. –

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