Выравнивание (C11)
Одной из низкоуровневых особенностей C является возможность указать точное выравнивание объектов в памяти, чтобы использовать все возможности аппаратной архитектуры.
Процессоры считывают и записывают память более эффективно, если данные хранятся по адресу, который кратен размеру данных. Например, доступ к 4-байтовому целому числу осуществляется более эффективно, если он хранится по адресу, кратному 4. Если данные не выровнены, ЦП приходится вычислять больше адресов для обращения к данным.
По умолчанию компилятор выравнивает данные по размеру: char
по 1-байтовой границе, short
по 2-байтовой границе, int
, long
и float
по 4-байтовой границе, double
по 8-байтовой границе и т. д.
Кроме того, выравнивание часто используемых данных в соответствии с размером строки кэш-памяти процессора может повысить производительность кэша. Например, предположим, вы определяете структуру, размер которой меньше 32 байтов. Вы можете использовать 32-байтовое выравнивание, чтобы гарантировать эффективное кэширование всех экземпляров структуры.
Как правило, вам не нужно беспокоиться о выравнивании. Компилятор обычно выравнивает данные по естественным границам с учетом характеристик целевого процессора и размера данных. Данные выравниваются по 4-байтовым границам на 32-разрядных процессорах и по 8-байтовым границам на 64-разрядных процессорах. Но в некоторых случаях можно повысить производительность или обеспечить экономное расходование памяти, настроив настраиваемое выравнивание для структур данных.
Используйте ключевое слово C11 _Alignof
, чтобы получить предпочтительное выравнивание типа или переменной, и _Alignas
, чтобы указать пользовательское выравнивание для переменной или определяемого пользователем типа.
Удобные макросы alignof
и alignas
, определенные в <stdalign.h>
, напрямую сопоставляются с _Alignof
и _Alignas
соответственно. Эти макросы соответствуют ключевым словам, используемым в C++. Поэтому использование макросов вместо ключевых слов C может оказаться полезным для переноса кода, если в вашем коде используется два языка.
alignas
и _Alignas
(C11)
Чтобы указать пользовательское выравнивание для переменной или определяемого пользователем типа, используйте alignas
или _Alignas
. Их можно применить к структуре, объединению, перечислению или переменной.
Синтаксис alignas
alignas(type)
alignas(constant-expression)
_Alignas(type)
_Alignas(constant-expression)
Замечания
_Alignas
нельзя использовать в объявлении typedef, битового поля, функции, параметра функции или объекта, объявленного с описателем register
.
Укажите выравнивание, которое является степенью двух, например 1, 2, 4, 8, 16 и т. д. Не используйте значение меньше размера типа.
Типы struct
и union
имеют выравнивание, равное значению самого крупного выравнивания любого элемента. В struct
добавляются байты заполнения, чтобы обеспечить соблюдение требований к выравниванию отдельных элементов.
Если в объявлении есть несколько описателей alignas
(например, struct
с несколькими элементами, которые включают разные описатели alignas
), выравнивание struct
будет по крайней мере равным значению самого большого описателя.
Пример alignas
В этом примере используется удобный макрос alignof
, так как его можно перенести в C++. Если вы используете _Alignof
, поведение будет аналогичным.
// Compile with /std:c11
#include <stdio.h>
#include <stdalign.h>
typedef struct
{
int value; // aligns on a 4-byte boundary. There will be 28 bytes of padding between value and alignas
alignas(32) char alignedMemory[32]; // assuming a 32 byte friendly cache alignment
} cacheFriendly; // this struct will be 32-byte aligned because alignedMemory is 32-byte aligned and is the largest alignment specified in the struct
int main()
{
printf("sizeof(cacheFriendly): %d\n", sizeof(cacheFriendly)); // 4 bytes for int value + 32 bytes for alignedMemory[] + padding to ensure alignment
printf("alignof(cacheFriendly): %d\n", alignof(cacheFriendly)); // 32 because alignedMemory[] is aligned on a 32-byte boundary
/* output
sizeof(cacheFriendly): 64
alignof(cacheFriendly): 32
*/
}
alignof
и _Alignof
(C11)
_Alignof
и его псевдоним alignof
возвращает выравнивание в байтах для указанного типа. Возвращает значение типа size_t
.
Синтаксис alignof
alignof(type)
_Alignof(type)
Пример alignof
В этом примере используется удобный макрос alignof
, так как его можно перенести в C++. Если вы используете _Alignof
, поведение будет аналогичным.
// Compile with /std:c11
#include <stdalign.h>
#include <stdio.h>
int main()
{
size_t alignment = alignof(short);
printf("alignof(short) = %d\n", alignment); // 2
printf("alignof(int) = %d\n", alignof(int)); // 4
printf("alignof(long) = %d\n", alignof(long)); // 4
printf("alignof(float) = %d\n", alignof(float)); // 4
printf("alignof(double) = %d\n", alignof(double)); // 8
typedef struct
{
int a;
double b;
} test;
printf("alignof(test) = %d\n", alignof(test)); // 8 because that is the alignment of the largest element in the structure
/* output
alignof(short) = 2
alignof(int) = 4
alignof(long) = 4
alignof(float) = 4
alignof(double) = 8
alignof(test) = 8
*/
}
Требования
Выполните сборку с помощью /std:c11
.
Пакет Windows SDK 10.0.20348.0 (версия 2104) или выше. Скачать последнюю версию Windows SDK можно здесь. Инструкции по установке и использованию пакета SDK для разработки C11 и C17 см. в статье Включение поддержки C11 и C17 в Visual Studio.
См. также
/std
(определение стандартной версии языка)
C++ alignof
и alignas
Обработка выравнивания данных компилятором