﻿# Урок 6\. Ошибки в 64\-битном коде

Исправление всех ошибок компиляции и предупреждений не будет означать работоспособность 64\-битного приложения\. И именно: описанию и диагностике 64\-битных ошибок будет посвящена основная часть уроков\. Также не надейтесь на помощь ключа [/Wp64](https://pvs-studio.ru/ru/blog/terms/0057/), который многими часто без оснований преподносится при обсуждениях в форумах как чудесное средство поиска 64\-битных ошибок\.

## Ключ /Wp64

Ключ /Wp64 позволяет программисту найти некоторые проблемы, которые могут возникнуть при компиляции кода для 64\-битных систем\. Проверка заключается в том, что типы, которые отмечены в 32\-битном коде ключевым словом \_\_w64, интерпретируются при проверке как 64\-битные типы\.

Например, пусть мы имеем следующий код:

```cpp
typedef int MyInt32;
#ifdef _WIN64
  typedef __int64 MySSizet;
#else
  typedef int MySSizet;
#endif
void foo() {
  MyInt32 value32 = 10;
  MySSizet size = 20;
  value32 = size;
}
```

Выражение "value32 \= size;" на 64\-битной системе приведет к урезанию значения, а, следовательно, к потенциальной ошибке\. Мы хотим это диагностировать\. Но при компиляции 32\-битного приложения все корректно, и мы не получим предупреждения\.

Для того чтобы подготовиться к 64\-битным системам, нам следует добавить ключ /Wp64 и вставить ключевое слово \_\_w64 при описании типа MySSizet в 32\-битном варианте\. В результате код станет выглядеть так:

```cpp
typedef int MyInt32;
#ifdef _WIN64
  typedef __int64 MySSizet;
#else
  typedef int __w64 MySSizet; // Add __w64 keyword
#endif
void foo() {
  MyInt32 value32 = 10;
  MySSizet size = 20;
  value32 = size; // C4244 64-bit int assigned to 32-bit int
}
```

Теперь мы получим предупреждение C4244, которое поможет подготовиться к переносу кода на 64\-битную платформу\.

Обратите внимание, что для 64\-битного режима компиляции ключ /Wp64 игнорируется, так как все типы уже имеют необходимый размер, и компилятор произведет необходимые проверки\. То есть при компиляции 64\-битной версии даже с выключенным ключом /Wp64 мы получим предупреждение C4244\.

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

Кстати, в Visual Studio 2008 ключ /Wp64 считается устаревшим, поскольку уже давно пора компилировать 64\-битные приложения, а не продолжать готовиться к этому\.

## 64\-битные ошибки

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

* код, основанный на некорректных представлениях о размере типов \(например, что размер указателя всегда равен 4 байтам\);
* код, обрабатывающий большие массивы, размер которых на 64\-битных системах превышает 2 гигабайта;
* код записи и чтения данных;
* код с битовыми операциями;
* код со сложной адресной арифметикой;
* старый код;
* и так далее\.

В конечном итоге все ошибки в коде, проявляющие себя при компиляции для 64\-битных систем, связаны с неточным следованием идеологии стандарта языка Си/Си\+\+\. Однако мы считаем нерациональным придерживаться позиции "пишите корректные программы, и тогда в них не будет 64\-битных ошибок"\. С этим нельзя поспорить, но и пользы от такой рекомендации для реальных проектов мало\. В мире накоплено огромное количество кода, который писался десятилетиями на языке Си/Си\+\+\. Задача этих уроков\- сформулировать все 64\-битные ошибки в виде набора паттернов, которые помогут выявить дефекты и дать рекомендации по их устранению\.

## Примеры 64\-битных ошибок

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

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

```cpp
size_t pointersCount = 100;
int **arrayOfPointers = (int **)malloc(pointersCount * 4);
```

Следующий пример демонстрирует ошибку в механизме чтения данных\. Данный код корректно работает в 32\-битном режиме и не обнаруживается компилятором\. Однако такой код некорректно прочитает данные сохраненные 32\-битной версией программы\.

```cpp
size_t PixelCount;
fread(&PixelCount, sizeof(PixelCount), 1, inFile);
```

## Комментарий искушенным программистам

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

Да, это именно так\! Мы помним про это\. Но мы не ставим целью рассматривать переносимость кода вообще\. В этих уроках мы хотим решить конкретную частную задачу, а именно: помочь разработчикам в освоении 64\-битных платформ, которые получают все большее распространение\.

Говоря о паттернах 64\-битных ошибках, мы будем рассматривать примеры кода, который корректно функционирует на 32\-битных системах, но может приводить к сбою при его переносе на 64\-битную архитектуру\.

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

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