﻿# Урок 21\. Паттерн 13\. Выравнивание данных

Процессоры работают эффективнее, когда имеют дело с правильно выровненными данными\. А некоторые процессоры вообще не умеют работать с не выровненными данными\. Попытка работать с не выровненными данными на процессорах [IA\-64](https://pvs-studio.ru/ru/blog/terms/0017/) \(Itanium\), как показано в следующем примере, приведет к возникновению исключения:

```cpp
#pragma pack (1) // Also set by key /Zp in MSVC
struct AlignSample {
  unsigned size;
  void *pointer;
} object;
void foo(void *p) {
  object.pointer = p; // Alignment fault
}
```

Если вы вынуждены работать с не выровненными данными на Itanium, то следует явно указать это компилятору\. Например, воспользоваться специальным макросом UNALIGNED:

```cpp
#pragma pack (1) // Also set by key /Zp in MSVC
struct AlignSample {
  unsigned size;
  void *pointer;
} object;
void foo(void *p) {
  *(UNALIGNED void *)&object.pointer = p; //Very slow
}
```

В этом случае компилятор сгенерирует специальный код, который будет работать с не выровненными данными\. Такое решение неэффективно, так как доступ к данным будет происходить в несколько раз медленнее\. Если целью является уменьшение размера структуры, то лучшего результата можно достичь, располагая данные в порядке уменьшения их размера\. Подробнее об этом будет рассказано в одном из следующих уроков\.

На архитектуре [x64](https://pvs-studio.ru/ru/blog/terms/0058/) при обращении к не выровненным данным исключения не возникает, но их также следует избегать\. Во\-первых, из\-за существенного замедления скорости доступа к таким данным, а во\-вторых, из\-за возможности переноса программы в будущем на платформу IA\-64\.

Рассмотрим еще один пример кода, не учитывающий выравнивание данных:

```cpp
struct MyPointersArray {
  DWORD m_n;
  PVOID m_arr[1];
} object;
...
malloc( sizeof(DWORD) + 5 * sizeof(PVOID) );
...
```

Если мы хотим выделить объем памяти, необходимый для хранения объекта типа MyPointersArray, содержащего 5 указателей, то мы должны учесть, что начало массива m\_arr будет выровнено по границе 8 байт\. Расположение данных в памяти на разных системах \(Win32/Win64\) показано на рисунке 1\.

![21_Pattern_13_Data_alignment_ru/image1.png](https://import.viva64.com/docx/blog/21_Pattern_13_Data_alignment_ru/image1.png)

_Рисунок 1\- Выравнивание данных в памяти на системах Win32 и Win64_

Корректный расчет размера должен выглядеть следующим образом:

```cpp
struct MyPointersArray {
  DWORD m_n;
  PVOID m_arr[1];
} object;
...
malloc( FIELD_OFFSET(struct MyPointersArray, m_arr) +
        5 * sizeof(PVOID) );
...
```

В приведенном коде мы узнаем смещение последнего члена структуры и суммируем это смещение с его размером\. Смещение члена структуры или класса можно узнать с использованием макроса offsetof или FIELD\_OFFSET\.

Всегда используйте эти макросы для получения смещения в структуре, не опираясь на знание размеров типов и выравнивания\. Пример кода с правильным вычислением адреса члена структуры:

```cpp
struct TFoo {
  DWORD_PTR whatever;
  int value;
} object;
int *valuePtr = 
  (int *)((size_t)(&object) + offsetof(TFoo, value)); // OK
```

Разработчиков Linux\-приложений может ждать еще одна неприятность, связанная с выравниванием\. О ней вы можете прочитать в нашем блоге в посте "[Изменения выравнивания типов и последствия](https://pvs-studio.ru/ru/blog/posts/cpp/0009/)"\.

## Диагностика

Поскольку работа с не выровненными данными не приводит к ошибке на архитектуре x64, а только к снижению производительности, инструмент [PVS\-Studio](https://pvs-studio.ru/ru/pvs-studio/) не предупреждает об упакованных структурах\. Но если для вас критична производительность приложения, рекомендуем просмотреть все места в программе, где используется "\#pragma pack"\. Для архитектуры IA\-64 данная проверка более актуальна, но анализатор PVS\-Studio пока не ориентирован на верификацию программ для IA\-64\. Если вы работаете с системами на базе Itanium и планируете приобрести PVS\-Studio, напишите нам, и мы обсудим вопросы адаптации этого инструмента к особенностям IA\-64\.

Инструмент PVS\-Studio позволяет обнаружить ошибки, связанные с вычислением размеров объектов и смещений\. Анализатор обнаруживает опасные арифметические выражения, содержащие в себе несколько операторов sizeof\(\), что свидетельствует о возможной ошибке\. Диагностическое сообщение имеет номер [V119](https://pvs-studio.ru/ru/docs/warnings/v119/)\.

Однако во многих случаях использование нескольких операторов sizeof\(\) в рамках одного выражения корректно и анализатор игнорирует подобные конструкции\. Пример безопасных выражений с несколькими операторами sizeof:

```cpp
int MyArray[] = { 1, 2, 3 };
size_t MyArraySize =
  sizeof(MyArray) / sizeof(MyArray[0]); //OK
assert(sizeof(unsigned) < sizeof(size_t)); //OK
size_t strLen = sizeof(String) - sizeof(TCHAR); //OK
```

## Приложение

На рисунке 2 представлены размеры типов и их выравнивание\. Для изучения размеров объектов и их выравнивания на различных платформах вы также можете воспользоваться примером кода, приведенным в записи блога "[Изменения выравнивания типов и последствия](https://pvs-studio.ru/ru/blog/posts/cpp/0009/)"\.

![21_Pattern_13_Data_alignment_ru/image3.png](https://import.viva64.com/docx/blog/21_Pattern_13_Data_alignment_ru/image3.png)

_Рисунок 2 \- Размеры типов и их выравнивание\._

_Авторы курса: Андрей Карпов \([karpov@viva64\.com](mailto:karpov@viva64.com)\), Евгений Рыжков \([evg@viva64\.com](mailto:evg@viva64.com)\)\._

_Правообладателем курса "Уроки разработки 64\-битных приложений на языке Си/Си\+\+" является ООО "Системы программной верификации"\. Компания занимается разработкой программного обеспечения в области анализа исходного кода программ\. [Сайт компании](https://pvs-studio.ru/ru/pvs-studio/)\._