Подтвердить что ты не робот

Какая разница между sizeof и alignof?

В чем разница между sizeof и align?

#include <iostream>

#define SIZEOF_ALIGNOF(T) std::cout<< sizeof(T) << '/' << alignof(T) << std::endl

int main(int, char**)
{
        SIZEOF_ALIGNOF(unsigned char);
        SIZEOF_ALIGNOF(char);
        SIZEOF_ALIGNOF(unsigned short int);
        SIZEOF_ALIGNOF(short int);
        SIZEOF_ALIGNOF(unsigned int);
        SIZEOF_ALIGNOF(int);
        SIZEOF_ALIGNOF(float);
        SIZEOF_ALIGNOF(unsigned long int);
        SIZEOF_ALIGNOF(long int);
        SIZEOF_ALIGNOF(unsigned long long int);
        SIZEOF_ALIGNOF(long long int);
        SIZEOF_ALIGNOF(double);
}

выводит

1/1 1/1 2/2 2/2 4/4 4/4 4/4 4/4 4/4 8/8 8/8 8/8

Я думаю, что я не понимаю, что такое выравнивание...?

4b9b3361

Ответ 1

Ну, "память" - это в основном огромный массив байтов. Тем не менее, для более крупных вещей, таких как целые, требуется хранить более 1 байт - например, 32-битное значение будет использовать 4 последовательных байта памяти.

Теперь модули памяти на вашем компьютере обычно не являются "байтами"; они также организованы с несколькими байтами "параллельно", например, блоками по 4 байта.

Для CPU это намного проще = более эффективно = более высокая производительность, чтобы не "пересекать" такие границы блока при чтении чего-то вроде целого:

memory byte    0 1 2 3     4 5 6 7       8 9 10 11
 integer       goooood
                   baaaaaaaaad

Это то, что говорит "выравнивание": выравнивание 4 означает, что данные этого типа должны (или должны, зависит от ЦП) быть сохранены, начиная с адреса, который кратен 4.

Вы заметили, что sizeof == alignof неверен; попробуйте структуры. Структуры также будут выровнены (потому что их отдельные члены должны заканчиваться на правильных адресах), но их размер будет намного больше.

Ответ 2

Оба оператора имеют принципиально разные вещи. sizeof задает размер типа (сколько памяти требуется), тогда как alignof указывает, на сколько байтов должен быть выровнен тип. Так получилось, что примитивы, которые вы тестировали, имеют требование выравнивания так же, как и их размер (что имеет смысл, если вы думаете об этом).

Подумайте, что произойдет, если у вас есть структура:

struct Foo {
     int a;
     float b;
     char c;
};

alignof(Foo) вернется 4.

Ответ 3

Старый вопрос (хотя и не отмечен как ответил..), но подумал, что этот пример делает немного более явным в дополнение к ответу Кристиана Штибера. Также ответ Meluha содержит ошибку, так как значение sizeof (S) равно 16 не 12.

// c has to occupy 8 bytes so that d (whose size is 8) starts on a 8 bytes boundary
//            | 8 bytes |  | 8 bytes  |    | 8 bytes |
struct Bad  {   char c;      double d;       int i;     }; 
cout << alignof(Bad) << " " << sizeof(Bad) << endl;   // 8 24

//             | 8 bytes |   |   8 bytes    |    
struct Good {   double d;     int i; char c;          };
cout << alignof(Good) << " " << sizeof(Good) << endl; // 8 16

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

Ответ 4

Что касается предоставленных ответов, кажется, что существует некоторая путаница относительно того, что такое выравнивание на самом деле. Вероятно, путаница возникает из-за двух типов выравнивания.

1. Расстановка элементов

Это качественная мера, которая определяет размер экземпляра в байтах для определенного порядка членов в типе структуры/класса. Как правило, компиляторы могут сжимать экземпляры структуры/класса, если элементы упорядочены по размеру в байтах в порядке убывания (то есть самые большие первые, самые маленькие последние элементы) в структуре. Рассмотрим:

struct A
{
  char c; float f; short s;
};

struct B
{
  float f; short s; char c;
};

Обе структуры содержат абсолютно одинаковую информацию. Ради этого примера; тип с плавающей запятой занимает 4 байта, короткий тип занимает 2, а символ занимает 1 байт. Однако первая структура A имеет элементы в случайном порядке, а вторая структура B упорядочивает элементы в соответствии с размером их байтов (в некоторых архитектурах это может отличаться, я предполагаю архитектуру процессора Intel x86 с 4-байтовым выравниванием в этом примере). Теперь рассмотрим размер структур:

printf("size of A: %d", sizeof (A)); // size of A: 12;
printf("size of B: %d", sizeof (B)); // size of B: 8;

Если вы ожидаете, что размер будет 7 байтов, вы предполагаете, что элементы упакованы в структуру с использованием выравнивания в 1 байт. Хотя некоторые компиляторы допускают это, в большинстве случаев компиляторы используют 4-байтовые или даже 8-байтовые выравнивания по историческим причинам (большинство ЦП работают с регистрами общего назначения типа DWORD (двойное слово) или QWORD (четыре слова)).

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

  1. Во-первых, каждый элемент, размер байта которого меньше, чем байтовое выравнивание, "объединяется" со следующим элементом (ами), если результирующий размер байта меньше или равен байтовому выравниванию. В структуре B члены s и c могут быть объединены таким образом; их объединенный размер составляет 2 байта для s + 1 байт для c == 3 байта & lt; = 4-байтовое выравнивание. Для структуры A такое объединение не может произойти, и каждый элемент эффективно использует 4 байта в упаковке структуры.

  2. Общий размер структуры снова дополняется, так что следующая структура может начинаться с границы выравнивания. В примере B общее число байтов будет равно 7. Следующая 4-байтовая граница лежит на байте 8, поэтому структура дополняется 1 байтом, чтобы разрешить распределение массивов в виде жесткой последовательности экземпляров.

Обратите внимание, что Visual C++/GCC допускает различные выравнивания 1 байта, 2 и более кратных 2 байта. Поймите, что это работает против вашей способности компилятора генерировать оптимальный код для вашей архитектуры. Действительно, в следующем примере каждый байт будет считан как один байт, используя однобайтовую инструкцию для каждой операции чтения. На практике аппаратное обеспечение будет по-прежнему извлекать всю строку памяти, которая содержит каждый прочитанный байт, в кэш и выполнять инструкцию 4 раза, даже если 4 байта находятся в одном и том же DWORD и могут быть загружены в регистр ЦП в одной инструкции.

#pragma pack(push,1)
struct Bad
{
  char a,b,c,d;
};
#pragma pack(pop)

2. Выравнивание размещения

Это тесно связано со вторым механизмом заполнения, поясненным в предыдущем разделе, однако выравнивания распределения могут быть указаны в вариантах функций распределения malloc/memalloc, например, станд :: aligned_alloc(). Следовательно, можно выделить объект на другой (обычно более высокой, кратной 2) границе выравнивания, чем предполагает выравнивание байтов структуры/типа объекта.

size_t blockAlignment = 4*1024;  // 4K page block alignment
void* block = std::aligned_alloc(blockAlignment, sizeof(T) * count);

Код будет размещать блок экземпляров типа T по адресам, число которых заканчивается на 4096.

Причина использования таких выравниваний распределения опять-таки чисто архитектурная. Например, чтение и запись блоков с выровненных по страницам адресов быстрее, потому что диапазон адресов хорошо вписывается в слои кэша. Диапазоны, которые разбиты по разным "страницам", удаляют кэш при пересечении границы страницы. Разные носители (архитектура шин) имеют разные шаблоны доступа и могут получать выгоду от разных выравниваний. Как правило, выравнивания страниц размером 4, 16, 32 и 64 К нередки.

Обратите внимание, что языковая версия и платформа обычно предоставляют конкретный вариант таких выровненных функций выделения. Например, функция posix_memalign, совместимая с Unix/Linux, возвращает память по аргументу ptr и возвращает ненулевые значения ошибок в случае сбоя.

  • int posix_memalign (void ** memptr, выравнивание size_t, размер size_t); //POSIX (Linux/UX)
  • void * align_alloc (выравнивание size_t, size_t size); //C++ 11
  • void * std::align_alloc (выравнивание size_t, size_t size); //C++ 17
  • void * align_malloc (size_t size, size_t alignment);MicrosoftVS2019

Ответ 5

Значение alignof совпадает с значением sizeof для базовых типов.

Разница заключается в использовании определенных типов данных, таких как использование struct; для, например,

typedef struct { int a; double b; } S;
//cout<<alignof(s);                              outputp: 8;
//cout<<sizeof(S);                               output: 12;

поэтому значение sizeof является общим размером, необходимым для данного типа данных; а выравнивание - это требование выравнивания самого большого элемента в структуре.

Использование alignof: выделяет память на определенной границе выравнивания.

Ответ 7

Какая разница между sizeof и alignof?

Оба являются операторами. Оба возвращают тип size_t.

sizeof - это размер в "байтах" объекта - объем памяти, необходимый для его кодирования.

alignof - это требование выравнивания адреса в "байтах" объекта. Значение 1 подразумевает отсутствие ограничений на выравнивание. 2 подразумевает, что адрес должен быть четным адресом. 4 подразумевает, что адрес должен быть четырехугольным. и т.п.

Когда делается попытка ссылки на объект, которая не соответствует требованию выравнивания, результатом является неопределенное поведение.
Примеры:
, Доступ может работать, только медленнее.
, Попытка доступа может убить программу.

// Assume alignof(int) --> 2
char a[4];   // It is not known that 'a' begins on an odd or even address
int *p = a;  // conversion may fail
int d = *p;  // *p is UB.

Пример расширения и вывода кода OP.

  SIZEOF_ALIGNOF(double);
  SIZEOF_ALIGNOF(complex double);
  SIZEOF_ALIGNOF(div_t);
  SIZEOF_ALIGNOF(max_align_t);

8/8
16/8
8/4
32/16