Почему размер структуры меняется в зависимости от того, используется ли начальное значение?

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

#include <cstdint>

struct B1 { int x; short y = 0; };
struct D1 : B1 { short z; };
struct B2 { int x; short y; };
struct D2 : B2 { short z; };
static_assert(sizeof(D1) == sizeof(D2));

GCC 14.2 (и Clang 18.0.1) терпит неудачу:

<source>:7:26: error: static assertion failed
    7 | static_assert(sizeof(D1) == sizeof(D2));
      |               ~~~~~~~~~~~^~~~~~~~~~~~~
<source>:7:26: note: the comparison reduces to '(8 == 12)'

Каково объяснение такого поведения?

«D2 и B2 имеют одни и те же элементы данных». Это не так. Вы имеете в виду D1 и D2?

Drew Dormann 19.08.2024 18:35

Очевидно, D1 и D2 имелись в виду, отредактированные.

john 19.08.2024 18:41

Макет классов, используемый GCC и Clang (в обычном режиме), определяется ABI Itanium C++: itanium-cxx-abi.github.io/cxx-abi/abi.html#class-types Однако он довольно плотный. , и я не пробираюсь через это. Их ключевое отличие, вероятно, заключается в том, что B2 является POD, а B1 — нет.

Sebastian Redl 19.08.2024 18:42
static_assert проходит, если все в B2 отмечено private. Это забавная головоломка.
Stephen Newell 19.08.2024 18:42

Любопытный! B2 — стандартная и тривиальная компоновка. B1 — стандартная компоновка и нетривиальная. D2 имеет нестандартную компоновку и тривиален. D1 — нестандартная компоновка и нетривиальная.

Eljay 19.08.2024 19:08
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
8
5
68
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это произвольный выбор ABI для совместимости с C.

В ABI Itanium C++, реализованном здесь с помощью GCC и Clang, B2 считается POD для целей макета, а B1 — нет. (Это разумно, поскольку B2 также является допустимым типом C, а B1 — нет.)

ABI указывает, что заполнение хвоста типа будет повторно использоваться только в том случае, если он не является POD для целей макета. (Так, например, memcpy/memset, как правило, используется в C, безопасно для подобъекта базового класса, если тип базового класса - POD для целей макета и не перезаписывает какие-либо члены производного класса. А в C компилятору всегда разрешено перезаписывать заполнение, если член изменен, поэтому даже без вызовов memcpy и т. д. использование подобъекта базового класса в C в противном случае было бы несовместимо с C++.)

Оба B1 и B2 имеют двухбайтовое заполнение хвоста. D1 будет повторно использовать его, чтобы он соответствовал члену z, а D2 не будет и вместо этого оставит два байта заполнения пустыми. Поскольку размер всего класса должен быть кратен его выравниванию, что 4 из-за члена int, D2 тогда также необходимо добавить два дополнительных новых байта заполнения хвоста.


Или, если быть более точным: мне кажется, что Itanium ABI четко не указывает, делает ли инициализатор элемента по умолчанию тип не POD для целей макета. Он использует определение POD в C++03, которое, очевидно, не знает об инициализаторах членов по умолчанию, которые были представлены в C++11. Однако кажется, что GCC и Clang, по крайней мере, согласились, что инициализатор члена по умолчанию должен сделать класс не POD для целей макета, и в целом имеет смысл, что более поздним расширениям языка C++ не нужно сильно беспокоиться о совместимости с C. (Можно спорить в любом направлении, но, по крайней мере, определение с инициализатором элемента по умолчанию не может использоваться в C без изменений.)

Я не уверен, почему это меняет ситуацию. Ни D1, ни D2 не являются допустимыми структурами C, так не может ли он все равно повторно использовать заполнение? Или B2, являющийся POD, означает «вы не должны повторно использовать дополнение ни при каких обстоятельствах»?

Yksisarvinen 19.08.2024 18:46

@Yksisarvinen С помощью этой спецификации вы можете безопасно создать объект D2 и передать его как B2* в функцию C, которая может перезаписать его заполнение. (Кстати, не только через memcpy/memset. В C компилятору всегда разрешено перезаписывать дополнение при любой модификации члена.) Но для D1/B1 это не проблема, потому что определение B1 невозможно даже проанализировать в C. .

user17732522 19.08.2024 18:49

@Yksisarvinen, но B2 является допустимой структурой C. Если sizeof(D2) == 8, то memcpy/memset в B2 перезапишет члены производного класса, даже если сам D2 нельзя будет использовать в коде C.

MarkB 19.08.2024 18:49

@MarkB • Члены D2 не «упакованы» в заполнение B2. Если процедура C использует memset больше, чем sizeof(B2), значит, она делает что-то нехорошее.

Eljay 19.08.2024 19:13

Другие вопросы по теме