Поделиться через


Выравнивание (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
Обработка выравнивания данных компилятором