Расширения GCC: Как узнать насколько выравнивать типы данных или переменные
Используя ключевое слово __alignof__ можно узнать какое выравнивание будет выполнено c объектом или с его типом, или просто произведено минимальное выравнивание в соответствие с максимально возможным для них размером. Синтаксис использования достаточно прост и схож с sizeof.
Для примера, если целевая машина требует, чтобы значение double было выравнено к 8-ми байтовой границе, __alignof__ (double) равно 8, что является истинным для многих машин архитектуры RISC. В тоже время для большинства традиционных архитектур __alignof__ (double) будет равно 4-ем (см. листинг 3 и 4) или даже 2-м байтам исходя из разрядности применяемой архитектуры центрального процессора.
Некоторые архитектуры процессоров никогда не требуют выравнивания, даже когда они обращаются к любому типу данных по нечетному адресу. Поэтому у них __alignof__ будет сообщать наименьшее выравнивание на которое способен GCC, что делается обычно в соответствие с назначением прикладного бинарного интефейса (Application Binary Interface – ABI).
Если операнд __alignof__ является скорее значением lvalue чем типом, тогда именно значение подлежит выравниванию в соответствие с его типом и учетом минимального выравнивания, указанного с использованием расширения __attribute__ компилятора GCC.
Когда говорят lvalue обычно имеется ввиду, что речь идет о выражении, обозначающие объект, а не значение. Соответственно, первые стоят слева от знака присваивания, а вторые – справа.
Рассмотрим код в листинге 1
Листинг 1 ( за основу взят код из примера Manual GCC.Inquiring on Alignment of Types or Variables) static struct foo { int x; short y; char z; float ax; long ay; } foo1; int main( void ) { printf("sizeof(foo1.x) = %2u => __alignof__(foo1.x) = %2u\n",sizeof(foo1.x), __alignof__(foo1.x)); printf("sizeof(foo1.y) = %2u => __alignof__(foo1.y) = %2u\n",sizeof(foo1.y), __alignof__(foo1.y)); printf("sizeof(foo1.z) = %2u => __alignof__(foo1.z) = %2u\n",sizeof(foo1.z), __alignof__(foo1.z)); printf("sizeof(foo1.ax) = %2u => __alignof__(foo1.ax) = %2u\n",sizeof(foo1.ax), __alignof__(foo1.ax)); printf("sizeof(foo1.ay) = %2u => __alignof__(foo1.ay) = %2u\n",sizeof(foo1.ay), __alignof__(foo1.ay)); return 0; } /* Команда для сборки: gcc -Wall -O2 -g -o test alignof-use.c */ |
После запуска (на архитектуре x86-32) исполняемого бинарного модуля test получим следующие результаты, представленные в листинге 2.
Листинг 2 user@debian:~$ ./test sizeof(foo1.x) = 4 => __alignof__ (foo1.x) = 4 sizeof(foo1.y) = 2 => __alignof__ (foo1.y) = 2 sizeof(foo1.z) = 1 => __alignof__ (foo1.z) = 1 sizeof(foo1.ax) = 4 => __alignof__ (foo1.ax) = 4 sizeof(foo1.ay) = 4 => __alignof__ (foo1.ay) = 4 |
Они подтверждают соответствие результатов между sizeof и __alignof__, но существует одна загвоздка, которая заключается в коде из листинга 3.
Листинг 3 static struct foo { double az; long long bx; long double by; } foo1; int main( void ) { printf("sizeof(foo1.az) = %2u => __alignof__(foo1.az) = %2u\n",sizeof(foo1.az), __alignof__(foo1.az)); printf("sizeof(foo1.bx) = %2u => __alignof__(foo1.bx) = %2u\n",sizeof(foo1.bx), __alignof__(foo1.bx)); printf("sizeof(foo1.by) = %2u => __alignof__(foo1.by) = %2u\n",sizeof(foo1.by), __alignof__(foo1.by)); fputc('\n',stdout); printf(" sizeof(foo1) = %2u => __alignof__ (foo1) = %2u\n", sizeof(foo1), __alignof__(foo1)); printf(" sizeof(struct foo) = %2u => __alignof__ (struct foo) = %2u\n", sizeof(struct foo), __alignof__( struct foo)); return 0; } |
После выполнения (на архитектуре x86-32) данного кода получим следующие не совсем внятные результаты, представленные в листинге 4.
Листинг 4 user@debian:~$ ./test sizeof(foo1.az) = 8 => __alignof__(foo1.az) = 4 sizeof(foo1.bx) = 8 => __alignof__(foo1.bx) = 4 sizeof(foo1.by) = 12 => __alignof__(foo1.by) = 4 sizeof(foo1) = 28 => __alignof__ (foo1) = 4 sizeof(struct foo) = 28 => __alignof__ (struct foo) = 4 |
Но, это лишь на первый взгляд, так как sizeof возвращает размер данных, в то время как __alignof__ возвращает минимально-возможное значение для выравнивания, равное 4 байтам, в соответсвии c назначением ABI для архитектуры x86-32.
Библиография
1. Manual GCC. Inquiring on Alignment of Types or Variables
3. Arithmetic operators in C/C++