Стандарт С++ для смещений элементов стандартной структуры макета

Гарантирует ли стандарт С++ 11, что все компиляторы будут выбирать одинаковые смещения памяти для всех членов данной стандартной структуры макета, предполагая, что все члены имеют гарантированные размеры (например, int32_t вместо int)?

То есть гарантирует ли C++11 для данного члена стандартной структуры макета, что offsetof будет давать одинаковое значение во всех компиляторах?

Если да, есть ли какая-либо спецификация того, каким будет это значение, например. как функция размера, выравнивания и порядка членов структуры?

Смещение элемента также зависит от выравнивания и заполнения, которые зависят от платформы (а также от типов элементов). Вы можете добиться большей согласованности, если компилятор упаковывает элементы или вставляете элементы заполнения (вместо компилятора).

Thomas Matthews 05.02.2023 21:35
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
90
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

Нет гарантии, что offsetof даст одинаковые значения для разных компиляторов.

Существуют гарантии относительно минимальных размеров типов (например, char >= 8 бит, short, int >= 16 бит, long >= 32 бит, long long >= 64 бит) и отношения между размерами 1 (sizeof(char) <= sizeof(short) < = sizeof(int) <= sizeof(long) <= sizeof(long long)).

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

Для любого struct/class первый (нестатический2) элемент должен быть в начале class/struct, и при отсутствии изменений видимости порядок гарантированно будет в порядке определения. Например:

struct { // same if you use `class`
    int a;
    int b;
};

Поскольку они оба общедоступны, a и b должны быть в таком порядке. Но:

struct {
    int a;
    int b;
private:
    int c;
};

Первый элемент (a) должен находиться в начале структуры, но из-за замены public на private компилятору (теоретически) разрешено размещать c перед b.

Хотя это правило со временем менялось. В C++98 даже пустой спецификатор видимости позволял переупорядочивать члены.

struct A {
    int a;
    int b;
public:
    int c;
};

public позволяет переставлять b и c, даже если они оба общедоступны. С тех пор он был ужесточен, так что это только элементы с разной видимостью, а в C++ 23 вся идея перестановки элементов на основе видимости ушла (и, по моему мнению, уже давно - я не думаю, что кто-то когда-либо использовал это, так что это всегда было правилом, которое вам нужно было знать, но оно никому не помогало).


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

@Aconcagua С C++ 23 это ограничение на тот же контроль доступа будет снято.

user17732522 05.02.2023 21:18

Нет, таких гарантий нет. Стандарт C++ явно предусматривает требования к заполнению и выравниванию для конкретного типа, во-первых, и это автоматически отменяет такого рода гарантии.

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

Мало того, что нет никаких гарантий, но на самом деле разные компиляторы действительно делают разные выборы на одних и тех же платформах (например, Microsoft C++ и, по крайней мере, некоторые порты gnu C++ используют разные размеры для bool на одном и том же оборудовании и ОС).

Jerry Coffin 05.02.2023 21:30

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

Peter 06.02.2023 03:21

Точно нет. Расположение памяти полностью зависит от реализации C++. Единственным исключением, только для классов стандартной компоновки, является то, что первый нестатический член данных или подобъект(ы) базового класса имеют нулевое смещение. Существуют также некоторые другие ограничения, например. из-за размеров и выравнивания подобъектов и ограничений на порядок адресов подобъектов, но ничего, что определяет конкретные смещения подобъектов.

Однако, как правило, компиляторы следуют некоторым спецификациям ABI для любой данной архитектуры/платформы, так что компиляторы для той же архитектуры/платформы, скорее всего, будут использовать один и тот же ABI и одинаковую структуру памяти (например, SysV x86-64 ABI вместе с Itanium C++ ABI в Linux). x86-64 по крайней мере для GCC и Clang).

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