Рассмотрим следующую структуру:
struct s {
int a, b;
};
Обычно 1 , эта структура будет иметь размер 8 и выравнивание 4.
Что если мы создадим два struct s
объекты (точнее, мы записываем в выделенное хранилище два таких объекта), причем второй объект перекрывает первый?
char *storage = malloc(3 * sizeof(struct s));
struct s *o1 = (struct s *)storage; // offset 0
struct s *o2 = (struct s *)(storage + alignof(struct s)); // offset 4
// now, o2 points half way into o1
*o1 = (struct s){1, 2};
*o2 = (struct s){3, 4};
printf("o2.a=%d\n", o2->a);
printf("o2.b=%d\n", o2->b);
printf("o1.a=%d\n", o1->a);
printf("o1.b=%d\n", o1->b);
Что-нибудь в этой программе неопределенное поведение? Если так, то где это становится неопределенным? Если это не UB, гарантированно всегда печатается следующее:
o2.a=3
o2.b=4
o1.a=1
o1.b=3
В частности, я хочу знать, что происходит с объектом, на который указывает o1
, когда o2
, который перекрывает его , написано. Разрешено ли по-прежнему иметь доступ к неблокированной части (o1->a
)? Является ли доступ к закрытой части o1->b
таким же, как и доступ к o2->a
?
Как здесь применяется эффективный тип ? Правила достаточно ясны, когда вы говорите о неперекрывающихся объектах и указателях, которые указывают на то же местоположение, что и последнее хранилище, но когда вы начинаете говорить об эффективном типе частей объектов или перекрывающихся объектов, это менее ясно.
Изменится ли что-нибудь, если вторая запись будет другого типа? Если участники скажут int
и short
, а не два int
с?
Вот Годболт , если вы хотите поиграть с ним там.
1 Этот ответ относится к платформам, где это не так: например, некоторые могут иметь размер 4 и выравнивание 2. На платформе, где размер и выравнивание были одинаковыми, этот вопрос не будет не применяется, поскольку выровненные, перекрывающиеся объекты были бы невозможны, но я не уверен, существует ли какая-либо подобная платформа.