Нарушает ли доступ к первому полю структуры через приведение C строгое сглаживание?

Нарушает ли этот код строгое сглаживание?

struct {int x;} a;
*(int*)&a = 3

Говоря более абстрактно, допустимо ли приведение типов между разными типами, если примитивные операции чтения/записи имеют правильный тип?


person Geoffrey Irving    schedule 17.03.2012    source источник
comment
V-таблицы потенциально стоят на первом месте, так что это территория UB.   -  person ildjarn    schedule 17.03.2012
comment
@ildjarn, vtables не существует в C   -  person bdonlan    schedule 17.03.2012
comment
@bdonlan: Это также помечено c++. ;-]   -  person ildjarn    schedule 17.03.2012
comment
Зачем тебе такая вещь? Просто сделайте &a.x.   -  person Jens Gustedt    schedule 17.03.2012
comment
Код будет выглядеть так только после оптимизации компилятора. Я знаю о синтаксисе a.x. :)   -  person Geoffrey Irving    schedule 17.03.2012
comment
В C++97 у вас нет какой-либо формальной гарантии того, что делает reinterpret_cast. Итак, с точки зрения языкового юриста, этот код не имеет определенного поведения в C++97. Мое замечание не имеет, повторяю, НУЛЕВОЙ актуальности для реального мира.   -  person curiousguy    schedule 21.07.2012
comment
Разве вы не включили language-lawyer намеренно?   -  person curiousguy    schedule 21.07.2012
comment
@curiousguy: Нет, я просто не знал о теге.   -  person Geoffrey Irving    schedule 21.07.2012


Ответы (1)


Во-первых, допустимо приведение в C. §6.7.2.1/13:

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

Правило псевдонимов выглядит следующим образом (§6.5/7):

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

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

Здесь вы будете получать к нему доступ с помощью указателей «типа, совместимого с эффективным типом объекта» и «агрегированного или объединенного типа, который включает один из вышеупомянутых типов среди своих членов», поэтому также нет проблем с псевдонимами. Таким образом, в C действительно совершенно законно обращаться к первому члену структуры путем приведения указателя на структуру к типу рассматриваемого члена.

Однако в C++ вы часто найдете виртуальные таблицы и другие вещи в начале объекта C++. Однако в вашем конкретном случае ваша структура имеет стандартный макет, поэтому это явно разрешено (§9.2/20 в n3290, спасибо Люку Дантону! - С++ 03, по-видимому, имеет аналогичное правило, выраженное в терминах объектов POD) .

person bdonlan    schedule 17.03.2012
comment
C++ определенно не стал бы замалчивать наличие vtable, но я применяю это только в тех случаях, когда таких странностей нет. - person Geoffrey Irving; 17.03.2012
comment
@Geoffrey: случаи без таких странностей. Ака тривиально копируемые типы? Ака что-то, что вы должны были упомянуть в своем вопросе? - person ildjarn; 17.03.2012
comment
Ну, в вопросе упоминается, что структура имеет одно поле int, что, среди прочего, подразумевает отсутствие vtable. Название уже довольно многословно. - person Geoffrey Irving; 17.03.2012
comment
В этом конкретном случае это будет тип POD - хотя не уверен, что те же гарантии применяются в С++ (нет под рукой спецификации) - person bdonlan; 17.03.2012
comment
Соответствующее правило для C++11 заключается в том, что указатель на объект типа структуры стандартного макета может быть reinterpret_cast указателем на его начальный член (9.2 Члены класса [class.mem], параграф 20 в n3290). Это правило (и все, что связано со стандартными типами макета) предназначено для имитации правил C, которые здесь цитируются. С++ 03 будет иметь аналогичные вещи, за исключением того, что это будет структура POD, а не структура стандартного макета. - person Luc Danton; 17.03.2012
comment
Вы должны доказать две разные вещи: (1) приведение определено и возвращает действительный указатель (2) указатель может быть разыменован, можно получить доступ к lvalue - person curiousguy; 21.07.2012