Выравнивание типа или переменной - это описание адресов памяти, которые может занимать переменная - адрес должен быть кратным выравниванию *. Однако для членов-данных адрес элемента-данных может быть любым K * alignof(S) + offsetof(S, member)
. Давайте определим выравнивание элемента данных как максимально возможное целое число E
, чтобы &some_s.member
всегда было кратным E
.
Учитывая тип S
с членом member
, пусть A = alignof(S), O = offsetof(S, member)
.
Допустимые адреса S{}.member
: V = K * A + O
для некоторого целого числа K
.
V = K * A + O = gcd(A, O) * (K * A / gcd(A, O) + O / gcd(A, O))
.
Для случая, когда K = 1
, других факторов не существует.
Таким образом, gcd(A, O)
является лучшим фактором, действительным для неизвестных K
.
Другими словами, "alignof(S.member)" == gcd(alignof(S), offsetof(S, member))
.
Обратите внимание, что это выравнивание всегда является степенью двойки, поскольку alignof(S)
всегда является степенью двойки.
*: В моем кратком наборе в стандарт я не смог найти эту гарантию, то есть адрес переменной может быть K * alignment + some_integer
. Однако это не влияет на конечный результат.
Мы можем определить макрос для вычисления выравнивания элемента данных:
#include <cstddef> // for offsetof(...)
#include <numeric> // for std::gcd
// Must be a macro, as `offsetof` is a macro because the member name must be known
// at preprocessing time.
#define ALIGNOF_MEMBER(cls, member) (::std::gcd(alignof(cls), offsetof(cls, member)))
Это гарантировано только для стандартных типов макетов, поскольку offsetof
гарантировано только для стандартных типов макетов . Если класс не является стандартным макетом, эта операция поддерживается условно.
Пример: * 1 047 *
#include <cstddef>
#include <numeric>
struct S1 { char foo; alignas(alignof(void *)) char member[sizeof(void *)]; };
struct S2 { char foo; char member[sizeof(void *)]; };
#define ALIGNOF_MEMBER(cls, member) (::std::gcd(alignof(cls), offsetof(cls, member)))
int f1() { return ALIGNOF_MEMBER(S1, member); } // returns alignof(void *) == 8
int f2() { return ALIGNOF_MEMBER(S1, foo); } // returns 8*
int f3() { return ALIGNOF_MEMBER(S2, member); } // returns 1
// *: alignof(S1) == 8, so the `foo` member must always be at an alignment of 8
Проводник компилятора