Расширения 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

2. Q & A: lvalue и rvalue

3. Arithmetic operators in C/C++

4. GCC bugzilla. Bug 52023

К оглавлению