Как обеспечить выравнивание члена по 4 байтам?

Чтобы использовать OSAtomicDecrement (атомарная операция для Mac), мне нужно предоставить SInt32, выровненный по 4 байтам.

Работает ли такая кулинария? Есть ли другой способ справиться с проблемами выравнивания?

struct SomeClass {
  SomeClass() {
    member_  = &storage_ + ((4 - (&storage_ % 4)) % 4);
    *member_ = 0;
  }

  SInt32 *member_;

  struct {
    SInt32 a;
    SInt32 b;
  } storage_;
};

person Lily B    schedule 23.01.2010    source источник


Ответы (6)


Если вы на Mac, это означает GCC. GCC может автоматически выравнивать переменные для вас:

  __attribute__((__aligned__(4))) int32_t member_;

Обратите внимание, что это не переносимо между компиляторами, так как это специфично для GCC.

person LiraNuna    schedule 23.01.2010
comment
В чем преимущество этого решения, поскольку оно менее переносимо (и дополнительные 4 байта используемого пространства в порядке)? - person Lily B; 23.01.2010
comment
-1: это гарантирует, что хранилище для указателя выровнено. Да, так как это выровнено, storage_ также будет выровнен, но это косвенно и сбивает с толку. Если вы хотите порекомендовать указать выравнивание, просто выровняйте переменную напрямую (отбросьте структуру и просто создайте член __attribute__((__aligned(4))) SInt32 a_; - person R Samuel Klatchko; 23.01.2010
comment
Дополнительные 4 байта НЕ используются, GCC просто перемещает класс в соответствии с вашими требованиями. Также НЕТ СПОСОБА сделать это надежно для разных компиляторов, у MSVC есть такая опция (__declspec align?). У вас есть особые потребности, которые может удовлетворить только компоновщик, и вы думаете, что сможете обойтись без опций, специфичных для компилятора? Указатель был остатком от вставленного кода, его там быть не должно (отредактировано). - person LiraNuna; 23.01.2010
comment
@LiraNuna в моем примере есть 4 дополнительных байта, а не в вашем. Извините за путаницу. Возможно, вы правы, возможно, я не смогу избежать зависимостей компилятора... - person Lily B; 23.01.2010
comment
Mac не обязательно означает GCC. Можно использовать clang/llvm, ICC поддерживает OS X, как и некоторые другие компиляторы. - person Stephen Canon; 24.01.2010
comment
@Gaspard: вам не нужен код, зависящий от компилятора. Участники уже согласованы. - person Richard Pennington; 24.01.2010
comment
@Richard: Ничто в спецификации C++ не говорит о том, что члены выровнены. Они стали таковыми из-за оптимизации компилятора для более быстрого доступа к памяти. Это никоим образом не гарантирует этого. В противном случае другой архитектуре/компилятору может быть лучше оптимизировать его. - person LiraNuna; 24.01.2010
comment
Он не говорит о стандарте. Он говорит о конкретном приложении и выравнивании на Mac. Я случайно знаю, как gcc и clang справляются с выравниванием для x86 под OSX, поэтому я подумал, что дать ему правильный ответ — это нормально. - person Richard Pennington; 24.01.2010
comment
Удаление моего отрицательного голоса, поскольку вы исправили проблему с указателем. Тем не менее, почему ваш пример все еще включает элемент storage_? - person R Samuel Klatchko; 24.01.2010
comment
@LiraNuna: Вы правы в том, что это не гарантируется спецификацией C++, но спецификация C++ — не единственный соответствующий документ. Правила OS X abi совершенно ясны, что ints по умолчанию выровнены по 4 байтам. - person Stephen Canon; 24.01.2010
comment
@LiraNuna Можете ли вы удалить хранилище и использовать вместо него int32_t (это тип, используемый в OSAtomic.h). - person Lily B; 24.01.2010
comment
@Richard Мне нужно быть абсолютно уверенным, что мой код не приведет к опасным результатам ни сейчас, ни в будущем, и явное требование выравнивания кажется хорошей идеей. - person Lily B; 24.01.2010
comment
@Гаспар: Все в порядке. Кажется, сегодня я уже израсходовал все свои 200 баллов. ;-) - person Richard Pennington; 24.01.2010

Я предполагаю, что любой SInt32 уже выровнен, даже на Mac.

Чтобы уточнить:

struct Foo {
    SInt32 member;
};

member всегда выравнивается правильно, если вы не упаковываете структуру и не ставите member после символа.

person Richard Pennington    schedule 23.01.2010
comment
Это будет иметь место на PowerPC, но НЕ на x86. PPC работает с обратным порядком байтов - слова должны быть выровнены, но с прямым порядком байтов это не гарантируется. - person LiraNuna; 23.01.2010
comment
Ни один компилятор не стал бы специально генерировать код, использующий смещенный доступ на платформе x86. Это приведет к серьезному снижению производительности. - person Richard Pennington; 23.01.2010
comment
@LiraNuna это неправда. Microsoft Visual C++ и GCC гарантируют выравнивание int по 4-байтовой границе на x86. Это задокументировано в MSDN, в случае Visual C++. - person mloskot; 23.01.2010
comment
Совершенно верно. Все 4-байтовые базовые типы по умолчанию выровнены по 4 байтам в OS X. - person Stephen Canon; 24.01.2010

ints по умолчанию выровнены по 4 байтам с любым из компиляторов OS X. Все, что вам нужно сделать, это не нарушать это выравнивание намеренно (например, делая неправильные приведения указателя, помечая вашу структуру как packed и т. д.).

person Stephen Canon    schedule 23.01.2010

Я ничего не знаю о программировании для Mac, но на мини-компьютерах, на которых я работал, указатели всегда выравнивались по 4-байтовым (словным) границам. IIRC, структуры тоже были. Выделенная память всегда была.

person Mick    schedule 23.01.2010

Если ваш компилятор поддерживает TR1 (или C++0x), вы можете использовать шаблон std::aligned_storage.

Чтобы выделить место для объекта с размером S и выравниванием A, вы можете выделить объект типа std::aligned_storage<S, A>::storage.

(Пространство имен может различаться в зависимости от компилятора. Я думаю, что TR1 не указывает, в какое пространство имен должны быть помещены расширения. В MSVC используется пространство имен std::tr1)

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

person jalf    schedule 23.01.2010

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

struct 
{
   Foo _hisFoo;
   unsigned int dummyAlignment : 0;
   Foo _myFoo;
}
person EvilTeach    schedule 23.01.2010
comment
Битовое поле нулевой длины дополняется следующей границей выравнивания его базового объявленного типа. Это приводит к тому, что следующий член начинается с границы байта (для битовых полей char), 2-байтовой границы (для коротких), 4-байтовой границы (для int или long) или 8-байтовой границы (для long long). Заполнение не происходит, если макет памяти предыдущего члена заканчивался на соответствующей границе. - person EvilTeach; 24.01.2010