﻿# Урок 5\. Сборка 64\-битного приложения

Хочется сразу предупредить читателя, что невозможно всесторонне описать процесс сборки 64\-битного приложения\. Настройки любого проекта достаточно уникальны, поэтому к адаптации настроек для 64\-битной системы всегда надо подходить внимательно\. В уроке будут описаны только общие шаги, которые важны для любого проекта\. Эти шаги подскажут вам, с чего начать процесс\.

## Библиотеки

Прежде чем попытаться собрать ваше 64\-битное приложение, проверьте, что установлены все необходимые версии 64\-битных библиотек и к ним корректно прописаны пути\. Например, 32\-битные и 64\-битные библиотечные lib файлы отличаются и, как правило, находятся в различных каталогах\. Исправьте найденные недочеты\.

_Примечание\. Если библиотеки представлены в исходных кодах, то должна присутствовать 64\-битная конфигурация проекта\. Учтите, что самостоятельно занимаясь модернизацией библиотеки для сборки ее 64\-битной версии, вы можете нарушить лицензионные соглашения\._

## Ассемблер

Visual C\+\+ не поддерживает 64\-битный встроенный ассемблер\. Вы должны использовать или внешний 64\-битный ассемблер \(например, MASM\), или иметь реализацию той же функциональности на языке Си/Си\+\+\.

## Примеры ошибок и предупреждений при компиляции

Начав сборку проекта, вы обнаружите большое количество ошибок компиляции и предупреждений, связанных с явным и неявным приведением типов\. Продемонстрируем пример возникновения одной из ошибок\. Пусть мы имеем код:

```cpp
void foo(unsigned char) {}
void foo(unsigned int) {}
void a(const char *str)
{
  foo(strlen(str));
}
```

Данный код успешно компилируется в 32\-битном режиме\. А в 64\-битном компилятор Visual C\+\+ выдаст сообщение:

```cpp
error C2668: 'foo' : ambiguous call to overloaded function
   .\xxxx.cpp(16): could be 'void foo(unsigned int)'
   .\xxxx.cpp(15): or 'void foo(unsigned char)'
   while trying to match the argument list '(size_t)'
```

Функция strlen\(\) возвращает тип [size\_t](https://pvs-studio.ru/ru/blog/terms/0044/)\. На 32\-битной системе тип size\_t совпадает с типом unsigned int и компилятор выбирает для вызова функцию void foo\(unsigned int\)\. В 64\-битном режиме типы size\_t и unsigned int не совпадают\. Тип size\_t становится 64\-битным, а тип unsigned int по\-прежнему остается 32\-битным\. В результате компилятор не знает, какой функции foo\(\) отдать предпочтение\.

Теперь рассмотрим пример предупреждения, выдаваемого компилятором Visual C\+\+ при сборке кода в 64\-битном режиме:

```cpp
CArray<char, char> v;
int len = v.GetSize();
warning C4244: 'initializing' : conversion from 'INT_PTR' to 'int',
                possible loss of data
```

Функция GetSize\(\) возвращает тип [INT\_PTR](https://pvs-studio.ru/ru/blog/terms/0024/), который совпадает с типом int в 32\-битном коде\. В 64\-битном коде тип INT\_PTR имеет размер 64 бита, и происходит неявное приведение этого типа к 32\-битному типу int\. При этом теряются значения старших битов, о чем и предупреждает компилятор\. Неявное приведение типа может привести к ошибке, если количество элементов в массиве превысит INT\_MAX\. Для устранения предупреждения и потенциальной ошибки следует использовать для переменной "len" тип INT\_PTR или [ptrdiff\_t](https://pvs-studio.ru/ru/blog/terms/0041/)\.

**Не торопитесь исправлять предупреждения** до того, как познакомитесь с паттернами 64\-битных ошибок\. Вы можете случайно, не исправив ошибку, замаскировать ее и тем самым сделать ее обнаружение в дальнейшем более сложной задачей\. О паттернах 64\-битных ошибок, методах их обнаружения и исправления вы сможете познакомиться в последующих уроках\. Также вы можете обратиться к следующим статьям: "[20 ловушек переноса Си\+\+ \- кода на 64\-битную платформу](https://pvs-studio.ru/ru/blog/posts/cpp/a0004/)", "[64\-битный конь, который умеет считать](https://pvs-studio.ru/ru/blog/posts/cpp/a0043/)"\.

## Типы size\_t и ptrdiff\_t

Поскольку большинство ошибок и предупреждений на этапе компиляции будет связано с несовместимостью типов данных, рассмотрим два типа size\_t и ptrdiff\_t, представляющих для нас наибольший интерес с точки зрения создания 64\-битного кода\. Если вы используете компилятор Visual C\+\+, то эти типы являются для него встроенными и подключения библиотечных файлов не требуется\. Если вы используете GCC, то вам потребуется использовать заголовочный файл stddef\.h\.

**size\_t** \- базовый беззнаковый целочисленный тип языка Си/Си\+\+\. Является типом результата, возвращаемого оператором sizeof\. Размер типа выбирается таким образом, чтобы в него можно было записать максимальный размер теоретически возможного массива любого типа\. Например, на 32\-битной системе size\_t будет занимать 32 бита, на 64\-битной \- 64 бита\. Другими словами, в переменную типа size\_t может быть безопасно помещен указатель\. Исключение составляют указатели на функции классов, но это особый случай\. Тип size\_t обычно применяется для счетчиков циклов, индексации массивов, хранения размеров, адресной арифметики\. Аналогами данному типу являются: SIZE\_T, DWORD\_PTR, WPARAM, ULONG\_PTR\. Хотя в size\_t можно помещать указатель, для этих целей лучше подходит другой беззнаковый целочисленный тип uintptr\_t, само название которого отражает эту возможность\. Типы size\_t и uintptr\_t являются синонимами\.

**ptrdiff\_t** \- базовый знаковый целочисленный тип языка Си/Си\+\+\. Размер типа выбирается таким образом, чтобы в него можно было записать максимальный размер теоретически возможного массива любого типа\. На 32\-битной системе ptrdiff\_t будет занимать 32 бита, на 64\-битной \- 64 бита\. Как и в size\_t в переменную типа ptrdiff\_t может быть безопасно помещен указатель, за исключением указателя на функцию класса\. Также ptrdiff\_t является результатом выражения, где один указатель вычитается из другого \(ptr1\-ptr2\)\. Тип ptrdiff\_t обычно применяется для счетчиков циклов, индексации массивов, хранения размеров, адресной арифметики\. Аналогами данному типу являются: SSIZE\_T, LPARAM, INT\_PTR, LONG\_PTR\. У типа ptrdiff\_t есть синоним intptr\_t, название которого лучше отражает, что тип может хранить в себе указатель\.

Типы size\_t и ptrdiff\_t были созданы для того, чтобы осуществлять корректную [адресную арифметику](https://pvs-studio.ru/ru/blog/terms/0005/)\. Долгое время было принято считать, что размер int совпадает с размером машинного слова \(разрядностью процессора\) и его можно использовать в качестве индексов для хранения размеров объектов или указателей\. Соответственно адресная арифметика также строилась с использованием типов int и unsigned\. Тип int используется в большинстве обучающих материалов по программированию на Си и Си\+\+ в телах циклов и в качестве индексов\. Практически каноническим является код:

```cpp
for (int i = 0; i < n; i++)
  a[i] = 0;
```

С развитием процессоров и ростом их разрядности стало нерационально дальнейшее увеличение размерностей типа int\. Причин для этого много: экономия используемой памяти, максимальная совместимость и так далее\. В результате появилось несколько [моделей данных](https://pvs-studio.ru/ru/blog/terms/0012/), описывающих соотношение размеров базовых типов языков Си и Си\+\+\. Соответственно, не так просто стало выбрать тип переменной для хранения указателя или размера объекта\. Чтобы наиболее красиво решить эту проблему , и появились типы size\_t и ptrdiff\_t\. Они гарантированно могут использоваться для адресной арифметики\. Теперь каноническим должен стать следующий код:

```cpp
for (ptrdiff_t i = 0; i < n; i++)
  a[i] = 0;
```

Именно он может обеспечить надежность, переносимость, быстродействие\. Почему \- станет ясно из дальнейших уроков\.

Описанные типы size\_t и ptrdiff\_t можно назвать [memsize](https://pvs-studio.ru/ru/blog/terms/0030/)\-типами\. Термин "memsize" возник как попытка лаконично назвать все типы, которые способны хранить в себе размер указателей и индексов самых больших массивов\. Под memsize\-типом следует понимать все простые типы данных языка Си/Си\+\+, которые на 32\-битой архитектуре имеют размер 32\-бита, а на 64\-битной архитектуре \- 64\-бита\. Примеры memsize\-типов: size\_t, ptrdiff\_t, указатели, SIZE\_T, LPARAM\.

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

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