Этот тип наказания хорошо определен? - PullRequest
0 голосов
/ 17 января 2019

У меня есть структура, как показано ниже.

struct result{
    int a;
    int b;
    int c;
    int d;
}

и объединение, как показано ниже.

union convert{
   int arr[4];
   struct result res;
}

и я набираю каламбур, как показано ниже.

  int arr1[4] = {1,2,3,5};
  union convert *pointer = (union convert *) arr1; // Here is my question, is it well defined?

  printf("%d %d\n", pointer->res.a, pointer->res.b);

Ответы [ 4 ]

0 голосов
/ 17 января 2019

Хорошо ли определен этот тип наказания?

struct result{
    int a,b,c,d;
}

union convert {
   int arr[4];
   struct result res;
}

int arr1[4] = {1,2,3,5};
union convert *pointer = (union convert *) arr1; 

(union convert *) arr1 риск сбоя выравнивания.

Указатель на тип объекта может быть преобразован в указатель на другой тип объекта. Если результирующий указатель неправильно выровнен для ссылочного типа, поведение не определено . C11dr §6.3.2.3 8

Не требуется, чтобы union convert и int имели одинаковое выравнивание Например, union convert требования могут превышать int.

Рассмотрим такую ​​возможность: arr1[] живет на int улице, где все адреса кратны 4. union и struct друзья живут на "кратной 8" улице. arr1[] может иметь адрес 0x1004 (не кратный 8).

В 2019 году сбои выравнивания чаще всего наблюдаются при char (требуется 1) и других типах, требующих 2 или более. В случае выбора OP, я сомневаюсь, что у реальной платформы будут проблемы с выравниванием, но неправильное выравнивание остается возможным.

Этот тип ударов не имеет четкого определения.


Дополнительные проблемы

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

@ Eric Postpischil комментарий о неправильном доступе с pointer->res.a добавляет больше причин для рассмотрения этого UB.

0 голосов
/ 17 января 2019

C не накладывает никаких правил о том, сколько заполнителей осталось между 2 последовательными элементами структуры.

Именно поэтому реализации определяют много директив #pragma - специально для изменения этого поведения.

Итак, как говорится в ответе Вирсавии, ...->b не определено.

Я ответил на тот же вопрос некоторое время назад, здесь .

0 голосов
/ 17 января 2019

Наказание указателя не является безопасным. Вместо этого используйте настоящий союзный удар.

Допущения: структура правильно упакована (нет заполнения между элементами)

#include <stdio.h>
#include <string.h>



struct __attribute__((packed)) result{
    int a;
    int b;
    int c;
    int d;
};

union convert{
   int arr[4];
   struct result res;
};

  volatile int arr1[4];

void foo(void)
{

  union convert cnv;

  memcpy(&cnv, (void *)arr1, sizeof(arr1));

  printf("%d %d\n", cnv.res.a, cnv.res.b);
}

все современные компиляторы оптимизируют вызов memcpy

https://godbolt.org/z/4qtRIF

.LC0:
        .string "%d %d\n"
foo:
        mov     rsi, QWORD PTR arr1[rip]
        xor     eax, eax
        mov     rdi, QWORD PTR arr1[rip+8]
        mov     edi, OFFSET FLAT:.LC0
        mov     rdx, rsi
        sar     rdx, 32
        jmp     printf
0 голосов
/ 17 января 2019

pointer->res.a в порядке, но поведение pointer->res.b не определено.

Может быть произвольное количество отступов между элементами a и b.

Некоторые компиляторы позволяют вам указать, что между членами нет заполнения, но, конечно, вы отказываетесь от переносимости.

...