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

Андрей Карпов
Статей: 370



В компиляторе Visual Studio C++ имеется warning C4311 ('variable' : pointer truncation from 'type' to 'type') предназначенный для выявления ошибок приведения указателя к 32-битным типам данных. В Intel C++ этому предупреждению соответствует warning #810. Пример обнаруживаемого дефекта:

void *ptr = x;
int y = (int)ptr;

В Win64 размер указателя стал 64-битным, а размер int остался по-прежнему 32 бита. Явное приведение типа обрежет значение указателя, что приведет к ошибке, если указатель ссылается на объект находящимся за пределами младших 4 Гбайт памяти (0x00000000FFFFFFFF).

Подобные ошибки коварны тем, что могут проявляться нерегулярно. Однако их можно легко обнаружить, просмотрев все предупреждения компилятора с номером C4311. Но вот с использованием самой этой проверки есть некоторый неприятный нюанс.

Если вы просто создадите 64-битный проект в Visual Studio 2008/2010, и напишите приведенный выше код, то вы не получите предупреждения C4311. Разберемся в причинах этой неожиданной ситуации.

В Visual Studio 2003/2005 имеется ключ /Wp64 предназначенный для выявления некоторых проблем, с которыми столкнется программист при сборке своего кода в 64-битном варианте. К группе этих предупреждений относится и предупреждение C4311. Если, создать проект в Visual Studio 2005, то даже в 32-битной конфигурации для строки int y = (int)ptr; будет сгенерировано предупреждение:

warning C4311: 'type cast' : 
pointer truncation from 'void *' to 'int'.

Ключ /Wp64 был предназначен, чтобы хоть как-то подготовить приложения к 64-битной платформе без необходимости создания 64-битной конфигурации. Однако, время "подготавливаться" прошло. Начиная с Visual Studio 2005, имеется 64-битный компилятор. Если есть желание поддерживать Win32 и Win64 платформу, то необходимо иметь две конфигурации проекта. В конфигурации x64 использовать ключ /Wp64 бессмысленно, и именно поэтому в Visual Studio 2008/2010 он объявлен устаревшим.

Все было бы хорошо, но как мне кажется разработчики Visual Studio допустили логическую ошибку. Если вы создадите новый проект в Visual Studio 2008/2010 или конвертируете старый проект в новый, то ключ /Wp64 будет не установлен. Это правильно. Если даже специально в "Additional Options" проекта прописать /Wp64, то будет выдано сообщение:

Command line warning D9035: 
option 'Wp64' has been deprecated 
and will be removed in a future release.

Юмор ситуации в том, что такие предупреждения как C4311, C4312, C4313 почему-то по-прежнему связаны с ключом /Wp64. И если его нет, то и нет этих предупреждений, хотя уровень их опасности Level 1.

Эти предупреждения вернутся, если включить /Wp64 и получить предупреждения D9035 об устаревшей опции. Другой вариант - включить /Wall. Сообщения будут выданы, но это путь, как вы понимаете, только для отважных. Видимо наиболее разумным вариантом является использование #pragma warning в stdafx.h.

Теперь перейдем к Intel C++. Начиная изучение вопроса, я ожидал, что в своем поведении относительно /Wp64 он эквивалентен Visual C++. Оказалось, что у него все по-своему. Ошибку приведения вида int y = (int)ptr; он обнаружил и без ключа /Wp64, выдав warning #810. А вот warning #967 (эквивалентный C4312) уже требует /Wp64. Получается, что в компиляторе Intel C++ также есть набор предупреждений связанный с /Wp64, но сам этот набор другой. Поскольку исторически сложилось, что с документацией по экзотическим особенностям Intel C++ туго, то я не нашел что именно включает /Wp64.

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

А в чем все-таки заковырка то? Прошу еще раз кратко.

Ответ. Если вы имеете проект для Visual Studio 2008/2010 и создадите 64-битную конфигурацию, то вы не увидите предупреждения для таких банальных ошибок как:

void *ptr = x;
int y = (int)ptr; // C4311

int i = x;
return (void*)i; // C4312

int * pI = 0;
printf("%d", pI); // C4313

И видимо некоторых других. Собирая этот же проект с помощью Intel C++, вы не увидите другой набор ошибок.

Чтобы получить все эти благотворные предупреждения вы должны явно самостоятельно включить их! Это не сложно сделать, при условии, что вы знаете про это. Я вот долгое время не знал, хотя интересуюсь тематикой разработки 64-битных приложений.

Подчеркну, что победа над этими предупреждениями вовсе не означает корректность 64-битной программы. Это только означает, что были исправлены наиболее очевидные дефекты. Так сказать "кто не спрятался, я не виноват". А вот для обнаружения "тех, кто спрятался" в большом проекте есть смысл использовать специализированные инструменты (Viva64).

Я конечно не первый заметил эту недоделку касательно 64-битных предупреждений. Совсем недавно здесь оставили комментарий на эту тему. Обратите внимание, что критика совсем свежая.

Из всего этого я могу сделать вывод, что люди только начинают интересоваться созданием Win64-приложений. Эту фразу я писал год назад, и сейчас опять повторил ее. Это очень странно. Я не верю, что можно разрабатывать нормальные программы, не интересуясь даже тем, где указатель запихивается в int. Отсутствие массовых обсуждений в интернете подобных вопросов меня крайне смущает. Я не понимаю, как устроен мир.



Найдите ошибки в своем C, C++, C# и Java коде

Предлагаем попробовать проверить код вашего проекта с помощью анализатора кода PVS-Studio. Одна найденная в нём ошибка скажет вам о пользе методологии статического анализа кода больше, чем десяток статей.

goto PVS-Studio;

Андрей Карпов
Статей: 370


Найденные ошибки

Проверено проектов
344
Собрано ошибок
12 970

А ты совершаешь ошибки в коде?

Проверь с помощью
PVS-Studio

Статический анализ
кода для C, C++, C#
и Java

goto PVS-Studio;