4
Урок 6. Ошибки в 64-битном коде Исправление всех ошибок компиляции и предупреждений не будет означать работоспособность 64-битного приложения. И именно описанию и диагностике 64-битных ошибок будет посвящена основная часть уроков. Также не надейтесь на помощь ключа /Wp64 , который многими часто без оснований преподносится при обсуждениях в форумах как чудесное средство поиска 64-битных ошибок. Ключ /Wp64 Ключ /Wp64 позволяет программисту найти некоторые проблемы, которые могут возникнуть при компиляции кода для 64-битных систем. Проверка заключается в том, что типы, которые отмечены в 32-битном коде ключевым словом __w64 интерпретируются при проверке как 64- битные типы. Например, пусть мы имеем следующий код: 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-битном варианте. В результате код станет выглядеть так: typedef int MyInt32; #ifdef _WIN64 typedef __int64 MySSizet;

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

Embed Size (px)

DESCRIPTION

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

Citation preview

Page 1: Урок 6. Ошибки в 64-битном коде

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

Исправление всех ошибок компиляции и предупреждений не будет означать работоспособность

64-битного приложения. И именно описанию и диагностике 64-битных ошибок будет посвящена

основная часть уроков. Также не надейтесь на помощь ключа /Wp64, который многими часто без

оснований преподносится при обсуждениях в форумах как чудесное средство поиска 64-битных

ошибок.

Ключ /Wp64

Ключ /Wp64 позволяет программисту найти некоторые проблемы, которые могут возникнуть при

компиляции кода для 64-битных систем. Проверка заключается в том, что типы, которые

отмечены в 32-битном коде ключевым словом __w64 интерпретируются при проверке как 64-

битные типы.

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

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-битном варианте. В результате

код станет выглядеть так:

typedef int MyInt32;

#ifdef _WIN64

typedef __int64 MySSizet;

Page 2: Урок 6. Ошибки в 64-битном коде

#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-битных систем,

связаны с неточным следованием идеологии стандарта языка Си/Си++. Однако мы считаем

Page 3: Урок 6. Ошибки в 64-битном коде

нерациональным придерживаться позиции "пишите корректные программы и тогда в них не

будет 64-битных ошибок". С этим нельзя поспорить, но и пользы от такой рекомендации для

реальных проектов мало. В мире накоплено огромное количество кода на языке Си/Си++,

который писался десятилетиями. Задача этих уроков сформулировать все 64-битные ошибки в

виде набора паттернов, которые помогут выявить дефекты и дать рекомендации по их

устранению.

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

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

понятно, что могут представлять из себя эти ошибки.

Пример использования магической константы 4, которая служит размером указателя, что

некорректно для 64-битного кода. Обратите внимание, данный код успешно функционировал в

32-битном варианте и не диагностируется компилятором как опасный.

size_t pointersCount = 100;

int **arrayOfPointers = (int **)malloc(pointersCount * 4);

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

работает в 32-битном режиме и не обнаруживается компилятором. Однако такой код

некорректно прочитает данные сохраненные 32-битной версией программы.

size_t PixelCount;

fread(&PixelCount, sizeof(PixelCount), 1, inFile);

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

Хочется заранее сделать комментарий о паттернах 64-битных ошибок и их примерах, которым

будет уделено большое количество уроков. Нам часто возражают, что это на самом деле не

ошибки 64-битности, а ошибки недостаточно корректно и недостаточно переносимо написанного

кода. И что многие ошибки можно обнаружить не только при переходе на 64-битную архитектуру,

но и просто на архитектуру с иными размерами базовых типов.

Да, это именно так! Мы помним про это. Но мы не ставим целью рассматривать переносимость

кода вообще. В этих уроках мы хотим решить конкретную частную задачу, а именно помочь

разработчикам в освоении 64-битных платформ, которые получают все большее распространение.

Говоря о паттернах 64-битных ошибках, мы будем рассматривать примеры кода, который

корректно функционирует на 32-битных системах, но может приводить к сбою при его переносе

на 64-битную архитектуру.

Авторы курса: Андрей Карпов ([email protected]), Евгений Рыжков ([email protected]).

Правообладателем курса "Уроки разработки 64-битных приложений на языке Си/Си++"

является ООО "Системы программной верификации". Компания занимается разработкой

программного обеспечения в области анализа исходного кода программ. Сайт компании:

http://www.viva64.com.

Page 4: Урок 6. Ошибки в 64-битном коде

Контактная информация: e-mail: [email protected], 300027, г. Тула, а/я 1800.