Инициализация объединения двух структур с общей начальной последовательностью - PullRequest
0 голосов
/ 15 февраля 2019

ВОПРОС : Если объединение содержит две структуры с общей начальной последовательностью совместимых типов, это поведение хорошо определено, если мы инициализируем некоторую часть начальной последовательности, используя одну структуру, а остальную часть части начальной последовательностииспользуя другую структуру?

Рассмотрим следующий фрагмент кода:

union u_t{
    struct {
        int i1;
        int i2;
    } s1;

    struct {
        int j1;
        int j2;
    } s2;
};

int main(){
    union u_t *u_ptr = malloc(sizeof(*u_ptr));
    u_ptr -> s1.i1 = 10;
    u_ptr -> s2.j2 = 11;

    printf("%d\n", u_ptr -> s2.j1 + u_ptr -> s1.i2); //prints 21
}

DEMO

Вопрос в том,Печатание 21 "поведение четко определено.Стандарт N1570 6.5.2.3(p6) определяет следующее:

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

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

Я думаю, что в итоге мы получим неопределенное поведение, поскольку мы только инициализировали s2.j2, а s2.j1 - нет, поэтому оно должно содержать неопределенное значение.

Ответы [ 2 ]

0 голосов
/ 15 февраля 2019

Что касается псевдонимов:

Общая начальная последовательность касается только псевдонимов двух структурных типов.Здесь это не проблема, и ваши две структуры являются даже совместимыми типами, и поэтому указатели на них могут быть псевдонимами без использования каких-либо уловок.Рассечение C11 6.2.7:

6.2.7 Совместимый тип и составной тип
Два типа имеют совместимый тип, если их типы совпадают./ - / Кроме того, два типа структуры, объединения или перечисления, объявленные в отдельных единицах перевода, совместимы, если их теги и члены удовлетворяют следующим требованиям:

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

Ни одна структура не объявлена ​​здесь с тегом.

Если оба выполняются где-либо в пределах их соответствующих единиц перевода, то применяются следующие дополнительные требования:

Они оба завершены (определены).

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

Это справедливо для этих структур.

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

Спецификаторы выравнивания не применяются.

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

Это верно.

Вывод: обе ваши структуры имеют совместимые типы.Это означает, что вам не нужны никакие уловки, такие как обычная начальная последовательность.Правило строгого псевдонима просто гласит (6.5 / 7):

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

Это здесь случай.

Кроме того, как уже упоминалось в других ответах, эффективный тип фактических данных здесь равен int, так каквыделенное хранилище не дает эффективного типа и поэтому становится первым типом, используемым для доступа к lvalue.Это также означает, что компилятор не может предположить, что указатели не будут псевдонимами.

Кроме того, правило строгого псевдонима дает исключение для lvalue-доступа членов структур и объединений:

агрегированный или объединенный тип, который включает в себя один из вышеупомянутых типов среди своих членов

И тогда у вас есть общая начальная последовательность поверх этого.Что касается псевдонимов, то это настолько четко определено, насколько это возможно.


Относительно типа наказания:

Ваша фактическая проблема, кажется, не имеет псевдонимов, но типа пробивая через союзы.Это смутно гарантировано C11 6.5.2.3/3:

Постфиксное выражение с последующим.оператор и идентификатор обозначают член структуры или объединенного объекта.Значение соответствует названному элементу (95) и является lvalue, если первое выражение является lvalue.

Это нормативный текст, и он плохо написан - никто не может понять, как предполагаются программы / компиляторывести себя исходя из этого.Информативное примечание 95) хорошо объясняет это:

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

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

Обратите внимание, что здесь C ++ отличается.

0 голосов
/ 15 февраля 2019

В стандарте C11 (n1570) в сноске на [6.5 Expressions]/6 указано, что:

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

И [6.5 Expressions]/6 утверждает, что:

6 Эффективным типом объекта для доступа к его сохраненному значению является объявленный тип объекта, если таковой имеется.Если значение сохраняется в объекте, у которого нет объявленного типа, через lvalue, имеющий тип, который не является символьным типом, то тип lvalue становится эффективным типом объекта для этого доступа и для последующих доступов, которые не изменяютсохраненное значение.

И вы также следуете установленным правилам в [6.5 Expressions]/7 при доступе к сохраненным значениям для печати в операторе printf.

Это в сочетании с цитатой, которую вы предоставили из N1570 6.5.2.3(p6), которая обеспечивает «Одна специальная гарантия для упрощения использования союзов», делает это четко определенным.

С практической стороны, если вы посмотритев сборке, сгенерированной , вы обнаружите, что именно это и происходит на самом деле.

        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     eax, 8
        mov     edi, eax
        call    malloc
        mov     qword ptr [rbp - 8], rax //Here
        mov     rax, qword ptr [rbp - 8] //Here
        mov     dword ptr [rax], 10      //Here 
        mov     rax, qword ptr [rbp - 8] //Here
        mov     dword ptr [rax + 4], 11  //Here 
        mov     rax, qword ptr [rbp - 8]
        mov     ecx, dword ptr [rax]
        mov     rax, qword ptr [rbp - 8]
        add     ecx, dword ptr [rax + 4]
        movabs  rdi, offset .L.str
        mov     esi, ecx
        mov     al, 0
        call    printf
        xor     ecx, ecx
        mov     dword ptr [rbp - 12], eax # 4-byte Spill
        mov     eax, ecx
        add     rsp, 16
        pop     rbp
        ret
.L.str:
        .asciz  "%d\n"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...