Может ли объект иметь более одного эффективного типа?

Рассмотрим следующий код на платформе, где ABI не вставляет заполнение в объединения:

union { int xi; } x;
x.xi = 1;

Я считаю, что вторая строка демонстрирует неопределенное поведение, поскольку она нарушает строгое правило псевдонимов:

Объект, на который ссылаетсяx.xi тот же объект, что и объект, на который ссылаетсяx, Оба имеют одинаковый регион хранения и срокобъект определено в ISO 9899: 2011 §3.15 как:

объект

1 область хранения данных в среде исполнения, содержимое которой может представлять значения

2 ПРИМЕЧАНИЕ. При ссылке объект может быть интерпретирован как имеющий определенный тип; см. 6.3.2.1.

Поскольку объект является не более чем областью хранения, я заключаю, что какx а такжеx.xi занимают одно и то же хранилище, это один и тот же объект.

эффективный тип изx являетсяunion { int xi; } как это тип, который был объявлен с. См. §6.5 ¶6:

6эффективный Тип объекта для доступа к его хранимому значению является объявленным типом объекта, если таковой имеется.87) Если значение сохраняется в объекте, у которого нет объявленного типа, через lvalue, имеющий тип, который не является символьным типом, то тип lvalue становится эффективным типом объекта для этого доступа и для последующих доступов, которые не изменяют сохраненное значение. Если значение копируется в объект, не имеющий объявленного типа, используяmemcpy или жеmemmoveили копируется как массив символьного типа, тогда эффективный тип модифицированного объекта для этого доступа и для последующих доступов, которые не изменяют значение, является эффективным типом объекта, из которого копируется значение, если оно имеет , Для всех других обращений к объекту, у которого нет объявленного типа, эффективный тип объекта - это просто тип lvalue, используемого для доступа.

87) Выделенные объекты не имеют объявленного типа.

Из формулировки ¶6 также ясно, что каждый объект может иметь только один эффективный тип.

В заявленииx.xi Я получаю доступx через lvaluex.xi набранныйint, Это не один из типов, перечисленных в §6.5-7:

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

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

88) Цель этого списка - указать те обстоятельства, при которых объект может или не может быть псевдонимом.

Поэтому вторая строка демонстрирует неопределенное поведение.

Поскольку эта интерпретация явно неверна, в чем заключается мое неправильное понимание стандарта?

Ответы на вопрос(2)

Решение Вопроса

x а такжеx.xi это один и тот же объект.

Союз является объектом, и он содержит объекты-члены1, Это разные объекты, каждый со своим типом.

1. (Цитируется из: ISO / IEC 9899: 20x1 6.2.5, типы 20)
Тип объединения описывает перекрывающийся непустой набор объектов-членов, каждый из которых имеет необязательно указанное имя и, возможно, отдельный тип.

 250105 авг. 2016 г., 11:13
@ JohannesSchaub-litb Цитата, которую вы разместили (см. Текст в скобках), отвечает на этот вопрос.
 fuz05 авг. 2016 г., 14:52
Интересно, что в этой цитате указано, что два объекта могут различаться, даже если они занимают одну и ту же область хранения.
 250105 авг. 2016 г., 10:52
@ JohannesSchaub-litb Предлагаю задать новый вопрос.
 Johannes Schaub - litb05 авг. 2016 г., 13:16
но эта цитата говорит «указатели на один и тот же объект (включая указатель на объект и подобъект в его начале», что является другим способом сказать «объект и подобъект в его начале - это один и тот же объект». Поправьте меня, если я неверно, но «включение» здесь означает, что регистр объекта и его подобъекта в начале включается в условие «указать на один и тот же объект». В противном случае, что означает «включение» после утверждения об одних и тех же объектах затем?
 250105 авг. 2016 г., 21:24
@FUZxxl Похоже, что это верно для агрегатных типов и объединений.
 supercat05 авг. 2016 г., 22:33
@FUZxxl: хранилище вернулось изmalloc() Выделение должно быть единым объектом, чтобы сделать арифметику указателей, сравнение указателей и работу memcpy (). Стандарт должен был определить новый термин, такой как «представление объекта», для своих ограничений типа доступа к указателю, причем последний термин относится к части объекта, к которой был получен доступ через определенный указатель, и к которой снова будет осуществляться доступ с использованием этого «того же самого». msgstr "указатель без каких-либо промежуточных типов.
 Johannes Schaub - litb05 авг. 2016 г., 11:12
Определение оператора равенства для указателей гласит: «Два указателя сравнивают равные, если и только если оба являются нулевыми указателями, оба являются указателями на один и тот же объект (включая указатель на объект и подобъект в его начале) ...». Это указывает на то, что подобъект и его родительский объект - это один и тот же объект.
 Johannes Schaub - litb05 авг. 2016 г., 10:51
Как это согласуется с определением «объекта» как «области хранения данных»? Означает ли это, что область хранения данных не обязательно является объектом / не обязательно единственным объектом? Т.е. эквивалентность только в одном направлении? Как мы можем знать тогда, когда и когда не область хранения данных является объектом?
 Keith Thompson05 авг. 2016 г., 23:21
@ JohannesSchaub-litb: я думаю, что использование слова «включая» в этом определении немного небрежно; «или» было бы более точным. Например, учитываяstruct { int x; int y; } obj;, obj а такжеobj.x являются (очевидно, я думаю) не один и тот же объект - но(void*)&obj == (void*)&obj.x, Есть интересный вопрос относительно того, является ли объект и начальный подобъектодного размера один и тот же объект, но определение равенства указателей на него не отвечает.

которые запрещают использование указателей для доступа к вещам других типов, термин «объект» относится к непрерывному распределению памяти. Каждая отдельная переменная автоматической или статической длительности является независимым объектом (поскольку реализация может произвольно распределить их по всей памяти), но любая область памяти, созданная malloc, будет одним объектом - по сути, типа «char []», независимо от того, как множество различных способов индексирования и доступа к ним.

Правила C89, касающиеся доступа к типу указателя, можно было бы сделать работоспособными, если бы, в дополнение к специальному правилу для типов символьных указателей, существовало соответствующее правило для соответственно выровненных объектов типов символьных массивов или для объектов без объявленного типа, которые были эффективно "char []". Интерпретация правил таким образом ограничит их применение объектами, которые объявили типы. Это позволило бы провести большую часть оптимизаций, которые были бы практичны в 1989 году, но по мере того, как компиляторы становились все более изощренными, становилось более желательным иметь возможность применять аналогичные оптимизации к выделенному хранилищу; поскольку правила не были установлены для этого, было мало ясности относительно того, что было или не было допустимо.

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

Авторы C99 вместо того, чтобы разрешить ситуацию путем определения директив для определения различных режимов псевдонимов, попытались «прояснить» его, добавив язык, который либо требует применения другого определения «объекта», чем тот, который использовался в другом месте, либо требует, чтобы каждый выделенная область содержит либо один массив одного типа, либо одну структуру, которая может содержать гибкий элемент массива. Последнее ограничение может быть использовано в языке, разрабатываемом с нуля, но фактически лишит законной силы огромное количество кода на языке Си. К счастью или к сожалению, однако, авторам стандарта пришлось сойти с рук от такой небрежной разработки, поскольку авторы компиляторов, по крайней мере до недавнего времени, были более заинтересованы в том, чтобы сделать то, что было необходимо для создания компилятораполезным чем при выполнении минимума, необходимого для соблюдения плохо написанного стандарта.

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

 hmijail06 авг. 2016 г., 09:45
Было бы неплохо узнать, понимает ли автор gcc как «качественный» компилятор или, как говорили другие, «состязательный» компилятор :). Или, другими словами, существует ли какой-либо текущий компилятор, который можно назвать «качественным компилятором» в соответствии с поведением, описанным в последнем абзаце?
 supercat08 авг. 2016 г., 16:30
@hmijail: GCC можно настроить для работы в качестве качественного компилятора за счет отключения многих оптимизаций, которые были бы полезны, если бы выполнялись менее опасно.
 supercat05 авг. 2016 г., 23:50
@FUZxxl: Возможно, авторы C99 действительно намеревались сделать Стандарт неприменимым к большому корпусу существующего кода, но если бы они это сделали, я бы подумал, что лучше сохранить код и отбросить Стандарт, чем наоборот. Я предпочитаю думать, что они предназначены для обеспечения возможности того, что области хранения внутри объекта приобретают типы в процессе использования. Это сделало бы намерение почти разумным, хотя то, как оно написано, напрасно калечит семантику, доступную программисту, способами, которые дают мало преимуществ оптимизации.
 fuz05 авг. 2016 г., 23:28
Хотя, что заставляет вас верить, что словообъект имеет другое значение в §6.5 (если я вас правильно понял)? Там, кажется, нет никакого текста, указывающего на эту интерпретацию.
 supercat05 авг. 2016 г., 23:46
@FUZxxl: Большая часть кода, написанного до 1999 года, будет использовать «malloc», чтобы получить большое выделение, а затем использовать арифметику указателей, чтобы перераспределить пространство из него для хранения многих видов структур. В некоторых случаях группы последовательных структур могут быть переданы в memcpy, fwrite и т. Д. Обеспечение совместимости такого кода с интерпретацией стандарта языка, который потребует, чтобы распределение malloc () обрабатывалось только как один объект одного типа. по сути, потребует полного переписывания, а многие виды ввода-вывода потребуют дублирования данных в «unsigned char []».
 supercat06 авг. 2016 г., 00:10
@FUZxxl: Если бы в стандарт были включены директивы, указывающие, когда необходимо переинтерпретировать или перерабатывать хранилище как разные типы (если хранилище переинтерпретируется, оно будет содержать битовый шаблон, записанный как старый тип, переинтерпретированный как новый тип; если он переработан , он будет содержать неопределенные битовые комбинации) и будет указывать, что код может включать только #pragma STDC ALIAS_EXPLICIT ", если все такие реинтерпретации, таким образом, помечены, что позволило бы гораздо больше оптимизаций, чем это возможно в настоящем стандарте, без нарушения какого-либо кода.

Ваш ответ на вопрос