7 шагов по переносу программы на 64-битную систему

    Аннотация


    В статье рассмотрены основные шаги, обеспечивающие корректный перенос 32-битных Windows приложений на 64-битные Windows системы. Хотя статья ориентирована на разработчиков, использующих язык Си/Си++ в среде Visual Studio 2005/2008, она будет полезна и другим разработчикам, планирующим перенос своих приложений под 64-битные системы.


    Введение


    В статье описаны основные моменты, с которыми сталкиваются разработчики, планирующие мигрировать 32-битные программы на 64-битные системы. Конечно, список рассмотренных вопросов не полон, но хочется надеяться, что со временем будет предложен расширенный вариант этой статьи. Автор будет благодарен отзывам, комментариям и вопросам, которые позволят улучшить информативность этой статьи.

    1. Шаг первый. 64-битность бывает разной.


    Давайте разберемся


    В рамках архитектуры вычислительной техники под термином "64-битный" понимают 64-битные целые и другие типы данных, имеющих размер 64 бита. Под «64-битными» системами могут пониматься 64-битные архитектуры микропроцессоров (например, EM64T, IA-64) или 64-битные операционные системы (например, Windows XP Professional x64 Edition) [1].
    AMD64 (она же x86-64, Intel 64, EM64T, x64) — 64-битная архитектура микропроцессора и соответствующий набор инструкций, разработанные компанией AMD [2]. Этот набор инструкций был лицензирован компанией Intel под названием EM64T (Intel64). Архитектура AMD64 представляет собой расширение архитектуры x86 с полной обратной совместимостью. Архитектура получила широкое распространение в качестве базы персональных компьютеров и рабочих станций.
    IA-64 — 64-битная микропроцессорная архитектура, разработанная совместно компаниями Intel и Hewlett Packard [3]. Реализована в микропроцессорах Itanium и Itanium 2 [4]. Архитектура используется в основном в многопроцессорных серверах и кластерных системах.
    AMD64 и IA-64 это две различные 64-битные архитектуры не совместимые между собой. Поэтому разработчикам следует сразу решить, необходимо ли поддерживать обе эти архитектуры или только одну. В большинстве случаев, если вы не разрабатываете узкоспециализированное программное обеспечение для кластерных систем или не реализуете свою высокопроизводительную СУБД, то с большой вероятностью вам необходимо реализовать поддержку только архитектуры AMD64, которая получила значительно большее распространение, чем IA-64. Особенно это относится к программному обеспечению для рынка персональных компьютеров, который почти на 100 процентов занят архитектурой AMD64.
    Далее в статье мы будем говорить только об архитектуре AMD64 (EM64T, x64), так как ее использование сейчас наиболее актуально для разработчиков прикладного программного обеспечения.
    Говоря о различных архитектурах, следует упомянуть о понятии "Модель данных". Под моделью данных следует понимать соотношения размерностей типов, принятых в рамках среды разработки. Для одной операционной системы могут существовать несколько средств разработки, придерживающихся разных моделей данных. Но обычно преобладает только одна модель, наиболее соответствующая аппаратной и программной среде. Примером может служить 64-битная операционная система Windows, в которой родной моделью данных является LLP64. Но для совместимости 64-битная система Windows поддерживает исполнение 32-битных программ, которые работают в режиме модели данных ILP32LL. В таблице N1 приведены сведения об основных моделях данных.

    Таблица N1. Модели данных

    Используемая модель данных накладывает отпечаток на процесс разработки 64-битных приложений, так как в коде программ необходимо учитывать разрядность используемых данных [5].

    2. Шаг второй. Выясните, нужен ли вам 64-битный вариант вашего продукта


    Начать освоение 64-битных систем следует с вопроса «А нужно ли нам пересобрать свой проект для 64-битной системы?». На этот вопрос надо обязательно дать ответ, но не торопясь, подумав. С одной стороны можно отстать от своих конкурентов, вовремя не предложив 64-битные решения. С другой — можно впустую потратить время на 64-битное приложение, которое не даст никаких конкурентных преимуществ.
    Перечислим основные факторы, которые помогут сделать вам выбор.

    2.1. Продолжительность жизненного цикла приложений


    Не следует создавать 64-битную версию приложения с коротким жизненным циклом. Благодаря подсистеме WOW64 старые 32-битные приложения достаточно хорошо работают на 64-битных Windows системах и поэтому делать программу 64-битной, которая через 2 года перестанет поддерживаться, смысла не имеет [6]. Более того, практика показала, что переход на 64-битные версии Windows затянулся и возможно большинство ваших пользователей в краткосрочной перспективе будут использовать только 32-битный вариант вашего программного решения.
    Если планируется длительное развитие и длительная поддержка программного продукта, то следует начинать работать над 64-битным вариантом вашего решения. Это можно делать неспешно, но учтите, что чем дольше у вас не будет полноценного 64-битного варианта, тем больше сложностей может возникать с поддержкой такого приложения, устанавливаемого на 64-битные версии Windows.

    2.2. Ресурсоемкость приложения


    Перекомпиляция программы для 64-битной системы позволит ей использовать огромные объемы оперативной памяти, а также убыстрит скорость ее работы на 5-15%. Убыстрение на 5-10% произойдет за счет использования архитектурных возможностей 64-битного процессора, например большего количества регистров. Еще 1%-5% прироста скорости обуславливается отсутствием прослойки WOW64, которая транслирует вызовы API между 32-битными приложениями и 64-битной операционной системой.
    Если ваша программа не работает с большими объемами данных (более 2GB) и скорость ее работы не критична, то переход на 64-битную в ближайшее время систему не столь актуален.
    Кстати, даже простые 32-битные приложения, могут получить преимущество от их запуска в 64-битной среде. Вы, наверное, знаете, что программа собранная с ключом /LARGEADDRESSAWARE:YES может выделять до 3-х гигабайт памяти, если 32-битная операционная система Windows запущена с ключом /3gb. Эта же 32-битная программа, запущенная на 64-битной системе может выделить почти 4 GB памяти (на практике около 3.5 GB).

    2.3. Разработка библиотек


    Если вы разрабатываете библиотеки, компоненты или иные элементы, с помощью которых сторонние разработчики создают свое программное обеспечение, то вы должны проявить оперативность в создании 64-битного варианта своей продукции. В противном случае, ваши клиенты, заинтересованные в выпуске 64-битных версий, будут вынуждены искать альтернативные решения. Например, некоторые разработчики программно-аппаратной защиты откликнулись с большой задержкой на появление 64-битных программ, что заставило ряд клиентов искать другие инструменты для защиты своих программ.
    Дополнительным преимуществом от выпуска 64-битной версии библиотеки является то, что вы можете продавать ее как отдельный продукт. Таким образом, ваши клиенты, желающие создавать как 32-битные, так и 64-битные приложения будут вынуждены приобретать 2 различные лицензии. Например, такая политика используется компанией Spatial Corporation при продаже библиотеки Spatial ACIS.

    2.4. Зависимость вашего продукта от сторонних библиотек


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

    2.5. Наличие 16-битных приложений


    Если в ваших решениях все еще присутствуют 16-битные модули, то пора от них избавиться. Работа 16-битных приложений в 64-битных версиях Windows не поддерживается.
    Здесь следует пояснить один момент, связанный с использованием 16-битных инсталляторов. Они до сих пор используются для установки некоторых 32-битных приложений. Создан специальный механизм, который на лету подменяет ряд наиболее популярных 16-битных инсталляторов на более новые версии. Это может вызвать неверное мнение, что 16-битные программы по-прежнему работают в 64-битной среде. Помните, это не так.

    2.6. Наличие кода на ассемблере


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

    3. Шаг третий. Инструментарий


    Если вы приняли решение о разработке 64-битной версии вашего продукта и готовы потратить на это время, это еще не гарантирует успех. Дело в том, что вы должны обладать всем необходимым инструментарием и здесь могут быть неприятные казусы.
    Самой простой, но и самой непреодолимой, может стать проблема отсутствия 64-битного компилятора. Статья пишется в 2009 году, но все еще нет 64-битного компилятора C++ Builder от Codegear [7]. Его выпуск ожидается только к концу этого года. Невозможно обойти подобную проблему, если конечно но переписать весь проект, например, с использованием Visual Studio. Но если с отсутствием 64-битного компилятора все понятно, то другие аналогичные проблемы могут оказаться более скрытными и вылезти уже на этапе работ по переносу проекта на новую архитектуру. Поэтому, хочется посоветовать заранее провести исследование, существуют ли все необходимые компоненты, которые потребуются для реализации 64-битной версии вашего продукта. Вас могут поджидать неприятные сюрпризы.
    Конечно, перечислить все, что может понадобиться для проекта здесь невозможно, но все-таки предложу список, который поможет вам соорентироваться и возможно вспомнить о других моментах, которые необходимы для реализации вашего 64-битного проекта:

    3.1. Наличие 64-битного компилятора


    Сложно что-то еще сказать о важности наличия 64-битного компилятора. Он просто должен быть.
    Если вы планируете разрабатывать 64-битные приложения с использованием последней версии (на момент написания статьи) Visual Studio 2008, то следующая таблица N2 поможет помочь определить, какая из редакций Visual Studio вам необходима.

    Таблица N2. Возможности различных редакций Visual Studio 2008


    3.2. Наличие 64-битных компьютеров под управлением 64-битных операционных систем


    Можно конечно использовать виртуальные машины для запуска 64-битных приложений на 32-битной технике, но это крайне неудобно и не обеспечит необходимого уровня тестовых испытаний. Желательно, чтобы в машинах было установлено не менее 4-8 гигабайт оперативной памяти.

    3.3. Наличие 64-битных вариантов всех используемых библиотек


    Если библиотеки представлены в исходных кодах, то должна присутствовать 64-битная конфигурация проекта. Самостоятельно заниматься модернизацией библиотеки для ее сборки под 64-битную систему может быть неблагодарным и сложным занятием, а результат может оказаться ненадежным и содержащим ошибки. Также вы можете нарушить этим лицензионные соглашения. Если вы используете библиотеки в виде бинарных модулей, то вы также должны узнать, существуют ли 64-битные модули. Вы не сможете использовать 32-битные DLL внутри 64-битного приложения. Можно создать специальную обвязку через COM, но эта будет отдельной большой, сложной задачей [8]. Также учтите, что приобретение 64-битной версии библиотеки может стоить дополнительных денег.

    3.4. Отсутствие встроенного кода на ассемблере


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

    3.5. Модернизация методологии тестирования


    Существенная переработка методологии тестирования, модернизация юнит-тестов, использование новых инструментальных средств. Более подробно об этом будет сказано ниже, но не забывайте учесть это на этапе оценки временных затрат на миграцию приложения на новую систему [10].

    3.6. Новые данные для тестирования


    Если вы разрабатываете ресурсоемкие приложения, потребляющие большой объем оперативной памяти, то вам необходимо позаботиться о пополнении базы тестовых входных данных. При нагрузочном тестировании 64-битных приложений желательно выходить за пределы 4 гигабайт потребляемой памяти. Многие ошибки могут проявиться только при таких условиях.

    3.7. Наличие 64-битных систем защиты


    Используемая система защиты, должна поддерживать 64-битные системы в полном необходимом вам объеме. Например, компания Aladdin достаточно быстро выпустила 64-битные драйвера для поддержки аппаратных ключей Hasp. Но очень долго отсутсвовала система автоматической защиты 64-битных бинарных файлов (программа Hasp Envelop). Таким образом, механизм защиты приходилось реализовывать самостоятельно внутри программного кода, что являлось дополнительной сложной задачей, требующей квалификации и времени. Не забывайте про подобные моменты, связанные с обеспечением защиты, системой обновлений и так далее.

    3.8. Инсталлятор


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

    4. Шаг четвертый. Настройка проекта в Visual Studio 2005/2008


    Создание 64-битной конфигурации проекта в Visual Studio 2005/2008 выглядит достаточно просто. Сложности будут подстерегать вас на этапе сборки новой конфигурации и поиска в ней ошибок. Для создания же 64-битной конфигурации достаточно выполнить следующие 4 шага:
    Запускаем менеджер конфигураций, как показано на рисунке N1:

    Рисунок 1. Запуск менеджера конфигураций


    В менеджере конфигураций выбираем поддержку новой платформе (рисунок N2):

    Рисунок 2. Создание новой конфигурации


    Выбираем 64-битную платформу (x64), а в качестве основы выбираем настройки от 32-битной версии (рисунок N3). Те настройки, которые влияют на режим сборки среда Visual Studio скорректирует сама.

    Рисунок 3. Выбираем x64 в качестве платформы и берем за основу конфигурацию Win32


    Добавление новой конфигурации завершено, и мы можем выбрать 64-битный вариант конфигурации и приступить к компиляции 64-битного приложения. Выбор 64-битной конфигурации для сборки показан на рисунке N4.

    Рисунок 4. Теперь доступна 32-битная и 64-битная конфигурация


    Если вам повезет, то дополнительно заниматься настройкой 64-битного проекта необходимости не будет. Но это сильно зависит от проекта, его сложности и количества используемых библиотек. Единственное, что стоит сразу изменить, это размер стека. В случае если в вашем проекте используется стек размером по умолчанию, то есть в 1 мегабайт, то есть смысл задать его размером в 2 мегабайта для 64-битной версии. Это не обязательно, но лучше заранее подстраховаться. Если у вас используется размер стека, отличный от размера по умолчанию, то есть смысл сделать его для 64-битной версии в 2 раза больше. Для этого в настройках проекта найдите и измените параметры Stack Reserve Size и Stack Commit Size.

    5. Шаг пятый. Компиляция приложения


    Здесь было бы хорошо рассказать о типичных проблемах, возникающих на этапе компиляции 64-битной конфигурации. Рассмотреть, какие проблемы возникают со сторонними библиотеками, рассказать, что компилятор в коде связанного с функциями WInAPI более не допустит помещения указателя в тип LONG и вам будет необходимо модернизировать свой код и использовать тип LONG_PTG. И многое, многое другое. К сожалению этого так много и ошибки так разнообразны, что нет возможности изложить это в рамках одной статьи и даже, пожалуй, книги. Вам придется самим просмотреть все ошибки, которые выдаст компилятор и новые предупреждения, которых ранее не было и в каждом отдельно случае разобраться, как модернизировать код.
    Частично облегчить жизнь может коллекция ссылок на ресурсы, посвященные разработке 64-битных приложений: http://www.viva64.com/links/64-bit-development/. Коллекция постоянно пополняется и автор будет благодарен читателям, если они пришлют ему ссылки на ресурсы, которые, по их мнению, заслуживают внимания.
    Остановимся здесь только на типах, которые могут представлять интерес для разработчиков при миграции приложений. Эти типы представлены в Таблице N3. Большинство ошибок при компиляции будет связано с использование именно этих типов.
    Тип Размерностьтипа наплатформеx32 / x64 Примечание
    int 32 / 32 Базовый тип. На 64-битных системах остался 32-битным.
    long 32 / 32 Базовый тип. На 64-битных Windows системах остался 32-битным. Учтите, что в 64-битных Linux системах этот тип был расширен до 64-бит. Не забывайте об этом если разрабатываете код, который должен работать компилироваться для Windows и для Linux систем.
    size_t 32 / 64 Базовый беззнаковый тип. Размер типа выбирается таким образом, чтобы в него можно было записать максимальный размер теоретически возможного массива. В тип size_t может быть безопасно помещен указатель (исключение составляют указатели на функции классов, но это особенный случай).
    ptrdiff_t 32 / 64 Аналогичен типу size_t, но является знаковым. Результат выражения, где один указатель вычитается из другого (ptr1-ptr2), как раз будет иметь тип ptrdiff_t.
    Указатель 32 / 64 Размер указателя напрямую зависит от разрядности платформы. Будьте аккуратны при приведении укзателей к другим типам.
    __int64 64 / 64 Знаковый 64-битный тип.
    DWORD 32 / 32 32-битный беззнаковый тип. Объявлен в WinDef.h как:typedef unsigned long DWORD;
    DWORDLONG 64 / 64 64-битный беззнаковый тип. Объявлен в WinNT.h как:typedef ULONGLONG DWORDLONG;
    DWORD_PTR 32 / 64 Беззнаковый тип, в который можно помещать указатель. Объявлен в BaseTsd.h как:typedef ULONG_PTR DWORD_PTR;
    DWORD32 32 / 32 32-битный беззнаковый тип. Объявлен в BaseTsd.h как:typedef unsigned int DWORD32;
    DWORD64 64 / 64 64-битный беззнаковый тип. Объявлен в BaseTsd.h как:typedef unsigned __int64 DWORD64;
    HALF_PTR 16 / 32 Половина указателя. Объявлен в Basetsd.h как:#ifdef _WIN64 typedef int HALF_PTR;#else typedef short HALF_PTR;#endif
    INT_PTR 32 / 64 Знаковый тип, в который можно помещать указатель. Объявлен в BaseTsd.h как:#if defined(_WIN64) typedef __int64 INT_PTR; #else typedef int INT_PTR;#endif
    LONG 32 / 32 Знаковый тип, который остался 32-битным. Поэтому во многих случаях теперь следует использовать LONG_PTR. Объявлен в WinNT.h как:typedef long LONG;
    LONG_PTR 32 / 64 Знаковый тип, в который можно помещать указатель. Объявлен в BaseTsd.h как:#if defined(_WIN64) typedef __int64 LONG_PTR; #else typedef long LONG_PTR;#endif
    LPARAM 32 / 64 Параметр для посылки сообщений. Объявлен в WinNT.h как:typedef LONG_PTR LPARAM;
    SIZE_T 32 / 64 Аналог типа size_t. Объявлен в BaseTsd.h как:typedef ULONG_PTR SIZE_T;
    SSIZE_T 32 / 64 Аналог типа ptrdiff_t. Объявлен в BaseTsd.h как:typedef LONG_PTR SSIZE_T;
    ULONG_PTR 32 / 64 Беззнаковый тип, в который можно помещать указатель. Объявлен в BaseTsd.h как:#if defined(_WIN64) typedef unsigned __int64 ULONG_PTR;#else typedef unsigned long ULONG_PTR;#endif
    WORD 16 / 16 Беззнаковый 16-битный тип. Объявлен в WinDef.h как:typedef unsigned short WORD;
    WPARAM 32 / 64 Параметр для посылки сообщений. Объявлен в WinDef.h как:typedef UINT_PTR WPARAM;

    Таблица N3. Типы представляющие интерес при переносе 32-битных программ на 64-битые Windows системы.

    6. Диагностика скрытых ошибок


    Если вы думаете, что после исправления всех ошибок компиляции будет получено долгожданное 64-битное приложение, то придется вас разочаровать. Самое сложное впереди. На этапе компиляции вами будут исправлены самые явные ошибки, которые смог обнаружить компилятор, которые в основном связаны с невозможностью неявного приведения типов. Но это верхушка айсберга. Основная часть ошибок скрыта. Эти ошибки с точки зрения абстрактного языка Си++ смотрятся безопасно или замаскированы явными приведениями типов. Таких ошибок в несколько раз больше, чем количество ошибок выявленных на этапе компиляции.
    На ключ /Wp64 надежды возлагать не следует. Это ключ часто преподносится как чудесное средство поиска 64-битных ошибок. В действительности ключ /Wp64 всего лишь дает возможность при компиляции 32-битного кода получить некоторые предупреждения, что в 64-битном режиме определенные участки кода будут некорректны. При компиляции 64-битного кода эти предупреждения будут выданы компилятором в любом случае. И поэтому при компиляции 64-битного приложения ключ /Wp64 игнорируется. И уж тем более этот ключ не поможет в поиске скрытых ошибок [11].
    Рассмотрим несколько примеров скрытых ошибок.

    6.1. Явное приведение типов


    Самый простой, но вовсе не самый легкий для обнаружения класс ошибок связан с явным приведением типов, при которых происходит обрезание значащих бит.
    Распространенным пример — приведение указателей к 32-битным типам при передачи их в функции, такие как SendMessage:
    MyObj* pObj = ...
    ::SendMessage(hwnd, msg, (WORD)x, (DWORD)pObj);
    



    Здесь явное приведение типа используется для превращения указателя в числовой тип. Для 32-битной архитектуры приведенный пример корректен, так как последний параметр функции SendMessage имеет тип LPARAM, который на 32-битной архитектуре совпадает с DWORD. Для 64-битной архитектуре использование DWORD ошибочно и должно быть заменено на LPARAM. Тип LPARAM имеет в зависимости от архитектуры размер 32 или 64 бита.
    Это простой случай, но часто приведение типа выглядит более изысканно и обнаружить его используя предупреждения компилятора или поиском по тексту программы невозможно. Явные приведения типов подавляют диагностику компилятора, поскольку они именно и предназначены, чтобы сказать компилятору что приведение типов корректно и программист взял на себя ответственность за безопасность кода. Явный поиск тоже не поможет. Типы могут быть не стандартные имена (заданные программистом через typedef), а способов осуществить явное приведение типов тоже не мало. Для надежной диагностики подобных ошибок необходимо использовать только специальный инструментарий, такой как анализаторы Viva64 или PC-Lint.

    6.2. Неявное приведение типов


    Следующий пример связан уже с неявным приведением типа, при котором также происходит потеря значащих бит. Код функции fread осуществляет чтение из файла, но некорректен при попытке чтения более 2 гигабайт данных на 64-битной системе.
    size_t __fread(void * __restrict buf, size_t size, 
        size_t count, FILE * __restrict fp);
    size_t
    fread(void * __restrict buf, size_t size, size_t count, 
        FILE * __restrict fp)
    {
    	int ret;
    	FLOCKFILE(fp);
    	ret = __fread(buf, size, count, fp);
    	FUNLOCKFILE(fp);
    	return (ret);
    }
    



    Функция __fread возвращает тип size_t, но для хранения количества прочитанных байт используется тип int. В результате при больших объемах читаемых данных функция может вернуть не то количество байт, которое на самом деле будет прочитано.
    Вы можете сказать, что это безграмотный код начинающих, что о таком приведении типа сообщит компилятор и что вообще такой код легко найти и поправить. Это теоретически. А практически в реальной жизни с большими проектами все может обстоять иначе. Этот пример взят из исходного кода FreeBSD. Ошибка была поправлена только в декабре 2008 года! Это притом, что первая (экспериментальная) 64-битная версия FreeBSD вышла еще в июне 2003 года.
    Вот исходный код до исправления:
    http://www.freebsd.org/cgi/cvsweb.cgi/src/lib/libc/stdio/fread.c?rev=1.14
    А вот исправленный вариант (декабрь 2008) года:
    http://www.freebsd.org/cgi/cvsweb.cgi/src/lib/libc/stdio/fread.c?rev=1.15

    6.3. Работа с битами, сдвиги


    Легко сделать ошибку в коде работящем с отдельными битами. Следующий тип ошибки связан с операциями сдвига. Рассмотрим пример:
    ptrdiff_t SetBitN(ptrdiff_t value, unsigned bitNum) {
      ptrdiff_t mask = 1 << bitNum;
      return value | mask;
    }
    



    Приведенный код работоспособен на 32-битной архитектуре и позволяет выставлять бит с номерами от 0 до 31 в единицу. После переноса программы на 64-битную платформу возникнет необходимость выставлять биты от 0 до 63. Но данный код никогда не выставит биты, с номерами 32-63. Обратите внимание, что «1» имеет тип int и при сдвиге на 32 позиции произойдет переполнение, как показано на рисунке 5. Получим мы в результате 0 (рисунок 5-B) или 1 (рисунок 5-C) зависит от реализации компилятора.

    Рисунок 5. A — Корректная установка 31-ого бита в 32-битном коде; B,C — Ошибка установки 32-ого бита на 64-битной системе (два варианта поведения)

    Для исправления кода необходимо сделать константу «1» того же типа, что и переменная mask:
    ptrdiff_t mask = ptrdiff_t(1) << bitNum;
    



    Заметим также, что неисправленный код приведет еще к одной интересной ошибке. При выставлении 31 бита на 64-битной системе результатом работы функции будет значение 0xffffffff80000000 (см. рисунок 6). Результатом выражения 1 << 31 является отрицательное число -2147483648. Это число представляется в 64-битной целой переменной как 0xffffffff80000000.

    Рисунок 6. Ошибка установки 31-ого бита на 64-битной системе


    6.4. Магические числа


    Много неприятностей могут доставить магические константы, то есть числа, с помощью которых задается размер того или иного типа. Правильным решением является использование для таких целей операторов sizeof(), но в большой программе вполне может затеряться старый кусочек кода, где твердо были уверены, что размер указателя был 4 байта, а в типе size_t всегда 32 бита. Обычно подобные ошибки выглядят следующим образом:
    size_t ArraySize = N * 4;
    size_t *Array = (size_t *)malloc(ArraySize);
    



    Основными числами, к которым следует отнестись с осторожностью при переходе на 64-битную платформу приведены в таблице N4.

    Таблица N4. Основные магические значения, опасные при переносе приложений с 32-битной на 64-битную платформу


    6.5. Ошибки использования 32-битных переменных в качестве индексов


    В программах обрабатывающих большие объемы данных могут встретиться ошибки связанные с индексацией больших массивов или возникнуть вечные циклы. Следующий пример содержит сразу 2 ошибки:
    const size_t size = ...;
    char *array = ...;
    char *end = array + size;
    for (unsigned i = 0; i != size; ++i)
    {
      const int one = 1;
      end[-i - one] = 0;
    }
    



    Первая ошибка заключается в том, что если размер обрабатываемых данных превысит 4 гигабайта (0xFFFFFFFF), то возможно возникновение вечного цикла, поскольку переменная 'i' имеет тип 'unsigned' и никогда не достигнет значения 0xFFFFFFFF. Я специально пишу, что возникновение возможно, но не обязательно оно произойдет. Это зависит от того, какой код построит компилятор. Например, в отладочном (debug) режиме вечный цикл будет присутствовать, а в release-коде зацикливание исчезнет, так компилятор примет решение оптимизировать код, используя для счетчика 64-битный регистр и цикл будет корректным. Все это добавляет путаницы, и код который работал вчера, неожиданно может перестать работать на следующий день.
    Вторая ошибка связана с проходом по массиву от конца к началу для чего используются отрицательные значения индексов. Приведенный код работоспособен в 32-битном режиме, но при его запуске на 64-битной машине на первой же итерации цикла произойдет доступ за границы массива и программа аварийно завершится. Рассмотрим причину такого поведения.

    Согласно правилом языка Си++ на 32-битной системе выражение "-i — one" будет вычисляться следующим образом (на первом шаге i = 0):
    1. Выражение "-i" имеет тип unsigned и имеет значение 0x00000000u.
    2. Переменная 'one' будет расширена от типа 'int' до типа unsigned и будет равна 0x00000001u. Примечание: Тип int расширяется (согласно стандарту языка Си++) до типа 'unsigned', если он участвует в операции, где второй аргумент имеет тип unsigned.
    3. Происходи операция вычитания, в котором участвуют два значения типа unsigned и результат выполнения операции равен 0x00000000u — 0x00000001u = 0xFFFFFFFFu. Обратите внимание, что результат имеет беззнаковый тип.
    4. На 32-битной системе обращение к массиву по индексу 0xFFFFFFFFu эквивалентно использованию индекса -1. То есть end[0xFFFFFFFFu] является аналогом end[-1]. В результате происходит корректная обработка элемента массива.

    В 64-битной системе в последнем пункте картина будет иной. Произойдет расширение типа unsigned до знакового ptrdiff_t и индекс массива будет равен 0x00000000FFFFFFFFi64. В результате произойдет выход за рамки массива.
    Для исправления кода необходимо использовать такие типы, как ptrdiff_t и size_t.

    6.6. Ошибки, связанные с изменением типов используемых функций


    Бывают ошибки, в которых, в общем, то никто не виноват, но они от этого не перестают быть ошибками. Представьте, что давным-давно в далекой галактике (в Visual Studio 6.0) был разработан проект, в котором присутствует класс CSampleApp, являющийся наследником от CWinApp. В базовом классе есть виртуальная функция WinHelp. Наследник перекрывает эту функцию и выполняет необходимые действия. Визуально это представлено на рисунке 7.

    Рисунок 7. Работоспособный корректный код, который создан в Visual Studio 6.0


    Затем проект переносится на Visual Studio 2005, где прототип функции WinHelp изменился, но этого никто не замечает, так как в 32-битном режиме типы DWORD и DWORD_PTR совпадают и программа продолжает корректно работать (рисунок 8).

    Рисунок 8. Не корректный, но работоспособный 32-битный код


    Ошибка ждет, чтобы проявить себя в 64-битной системе, где размер типов DWORD и DWORD_PTR различен (рисунок 9). Получается, что в 64-битном режиме классы содержат две РАЗНЫЕ функции WinHelp, что естественно некорректно. Учтите, что подобные ловушки могут скрываться не только в MFC, где часть функций изменили типы своих аргументов, но и в коде ваших приложений и сторонних библиотек.

    Рисунок 9. Ошибка проявляет себя в 64-битном коде


    6.7. Диагностика скрытых ошибок


    Примеры подобных 64-битных ошибок можно приводить и приводить. Тем, кто заинтересовался подобными ошибками и хочет более подобно узнать о них будет интересна статья «20 ловушек переноса Си++ — кода на 64-битную платформу» [12].
    Как видите, этап поиска скрытых ошибок представляет нетривиальную задачу, тем более что многие из них будут проявляться нерегулярно или только на больших входных объемах данных. Для диагностики подобных ошибок хорошо подходят статические анализаторы кода, так как они могут проверять весь код приложения, в не зависимости от входных данных и частоты выполнения его участков в реальных условиях. Использовать статический анализ есть смысл как на этапе переноса приложения на 64-битные платформы, чтобы найти большинство ошибок на самом начальном этапе, так и в дальнейшей разработке 64-битных решений. Статические анализ предупредит и научит программиста лучше понимать особенности ошибок связанных с 64-битной архитектурой и писать более эффективный код. Автор статьи является разработчиком одного из таких специализированных анализаторов кода, носящий название Viva64 [13]. Более подробно познакомиться с инструментом и скачать демонстрационную версию можно c сайта компании ООО «Системы программной верификации».
    В качестве справедливости следует сказать, что в таких анализаторах кода как Gimpel PC-Lint и Parasoft C++Test имеются наборы правил для диагностики 64-битных ошибок. Но, во-первых, это анализаторы общего назначения и правила диагностики 64-битных ошибок в них представлены слабо. Во-вторых они больше ориентированы на модель данных LP64 используемую в семействе операционных систем Linux, что снижает их пользу для Windows программ, где используется модель данных LLP64 [14].

    7. Шаг седьмой. Модернизация процесса тестирования


    Описанный в предыдущем разделе шаг поиска ошибок в программном коде необходимый, но недостаточный шаг. Ни один метод, в том числе статического анализа кода, не дает полной гарантии обнаружения всех ошибок и наилучший результат может быть достигнут только при комбинации различных методик.
    Если ваша 64-битная программа обрабатывает больший объем данных, чем 32-битная версия то необходимо расширить тесты, чтобы включить в них обработку данных объемом более 4 гигабайт. Эта та граница, за которой начинают проявлять себя многие 64-битные ошибки. Времени такие тесты могут занимать на порядок больше и к этому надо быть заранее готовым. Обычно тесты пишут так, чтобы обрабатывать в каждом тесте небольшое количество элементов и тем самым иметь возможность проходить все внутренние юнит-тесты, например? за несколько минут, а автоматические тесты (например, с использованием AutomatedQA TestComplete) за несколько часов. Функция сортировки на 32-битной системе, если она сортирует 100 элементов, почти с полной гарантией будет корректна себя вести на 100000 элементах. Но та же функция на 64-битной системе может подвести при попытке обработать 5 миллиардов элементов. Скорость выполнения юнит-теста может понизиться в миллионы раз. Не забудьте заложить стоимость адаптации тестов при освоении 64-битных систем. Одним из решений является разделение юнит-тестов на быстрые (работающие с малым объемом памяти) и медленные, обрабатывающих гигабайты и запускаемые, например, ночью. Автоматизированное тестирование ресурсоемких 64-битных программ можно построить на основе распределенных вычислений.
    PVS-Studio
    Static Code Analysis for C, C++, C# and Java

    Comments 51

      +16
      Эх… Это ограничение на размер поста… В «предпросмотре» все хорошо. А здесь обрезается. И ведь главное немножко не влезает. Напишу в виде комментария.

      Есть еще одна неприятная новость. Вам вряд ли удастся использовать инструменты, подобные BoundsChecker для поиска ошибок в ресурсоемких 64-битных программах, поглощающих большой объем памяти. Дела в колоссальном замедлении тестируемых программ, что делает такой подход крайне неудобным. Инструмент Parallel Inspector входящий в состав Intel Parallel Studio в режиме диагностики всех ошибок связанных с работой с памятью, замедлит скорость выполнения приложения в среднем в 100 раз (рисунок 10). Вполне есть вероятность, что тестируемый алгоритм, который работает 10 минут, придется оставлять на ночь и смотреть результаты работы только на следующий день. При этом я уверен, что Parallel Inspector в режиме поиска ошибок работы с памятью один из самых полезных и удобных инструментов. Просто надо быть готовым к изменению практики диагностики ошибок и закладывать это в планы освоения 64-битных систем.

      Рисунок 10. Окно настроек программы Parallel Inspector перед запуском приложения.


      Последнее. Не забудьте добавить тесты, проверяющие совместимость форматов данных между 32-битной и 64-битной версией. Совместимость данных при миграции достаточно часто нарушается из-за записи в файлы таких типов как size_t или long (в случае Linux систем).

      Библиографический список
      1. Wikipedia. 64-bit. http://www.viva64.com/go.php?url=203
      2. Wikipedia. AMD64. http://www.viva64.com/go.php?url=204
      3. Wikipedia. IA-64. http://www.viva64.com/go.php?url=205
      4. Wikipedia. Itanium. http://www.viva64.com/go.php?url=206
      5. Андрей Карпов. Забытые проблемы разработки 64-битных программ http://www.viva64.com/art-1-1-1609701563.html
      6. Wikipedia. WOW64. http://www.viva64.com/go.php?url=207
      7. Nick Hodges. The Future of the Delphi Compiler. http://www.viva64.com/go.php?url=208
      8. Mike Becker. Accessing 32-bit DLLs from 64-bit code. http://www.viva64.com/go.php?url=209
      9. Eric Palmer. How to use all of CPUID for x64 platforms under Microsoft Visual Studio .NET 2005. http://www.viva64.com/go.php?url=210
      10. Андрей Карпов, Евгений Рыжков. Поиск ловушек в Си/Си++ коде при переносе приложений под 64-битную версию Windows. http://www.viva64.com/art-1-1-329725213.html
      11. Андрей Карпов. 64 бита, /Wp64, Visual Studio 2008, Viva64 и все, все, все… http://www.viva64.com/art-1-1-253695945.html
      12. Андрей Карпов, Евгений Рыжков. 20 ловушек переноса Си++ — кода на 64-битную платформу. http://www.viva64.com/art-1-1-1958348565.html
      13. Евгений Рыжков. Viva64: что это и для кого? http://viva64.com/art-1-1-2081052208.html
      14. Андрей Карпов. Сравнение диагностических возможностей анализаторов при проверке 64-битного кода. http://www.viva64.com/art-1-1-1441719613.html
        +3
        Эпический пост, впрочем магически мой код обычно работал для x64 без изменений вообще…
          +2
          Работоспособность кода на 64-битность системе зависит от множества факторов. Основные: возраст и размер проекта, объем потребляемой памяти. Отсюда и сильные вариации. От простой перекомпиляции до многих месяцев устранения ошибок.
            –1
            мои игрушки перестали работать на 64 битных системах.
            только потому, что используемая библиотека dx8vb.dll перестала поддерживаться мелкософтом, была выкинута из DirectX и хваленая мелкософтовская обратная совместимость оказалась мифом ((

            сказать, что обидно это значит ничего не сказать — перетащить проекты из vb6 в vb2008 задача нетривиальная ((
              0
              Дык 8дх — старьё, заюзайте dx9
                +3
                Вы ругаете микрософт за неподдержку библиотеки для среды разработки, которая не выпускается уже лет 10 (vb6) минимум? :)
            +1
            Когда-то в своё время наткнулся на ваши статьи на viva64, прочитал все с интересом, теперь есть возможность сказать «спасибо, понравилось» :)
              –3
              Господи, как всё сложно то.
              Кстати, вполне себе давно существует 64битный gcc под Win
              sourceforge.net/projects/mingw-w64/files/
              Ну и большинство ловушек, в общем то, оставили бедным людям на радость создатели winAPI((( У меня лично ни разу ещё не возникло проблем с перекомпиляцией кода под другие платформы.
                0
                Можно пару-тройку примеров ловушек?
                –8
                >убыстрит скорость
                Вы и на c++ пишете также?
                  –2
                  CWinApp? MFC?

                  По-моему, в этом случае переход на x86-64 отличный повод не мучаться, а просто переписать весь UI на чем-нибудь адекватном :)
                    +1
                    Это совсем разные задачи. Одно дело — пересмотреть код на наличие «магических чисел» и проверить всякие сдвиги в памяти — и совсем другое дело переводить всю программу на «что-то адекватное». Трудозатраты могут отличаться в десятки раз. Высшее руководство на это не пойдет практически никогда.
                      –1
                      Ну и обойдут эту компанию конкуренты, использующие новые подходы и технологии. Банально окажется, что они будут реализовывать необходимую функциональность за месяц, а ваша компания, со старым подходом, за полгода
                        +1
                        Как, без сомнения, верно заметил уважаемый автор статьи, стоит заранее определиться с жизненным циклом приложения и целями, которые перед ним ставятся.

                        Есть целая пропасть вариантов от «давайте за два дня на коленке напишем что-нибудь, оно закроет наши текущие потребности и ладно» до «в этот продукт будут вложены миллионы и годы разработки с расчетом на то, что он станет флагманом в данной нише». Для каждого из этих (и всех промежуточных) вариантов есть свой оптимальный путь развития. Некоторые из них предполагают полный рефакторинг (и переход на х64), другие — нет.

                        Разговаривать о сферическом приложении в вакууме и как его верно было бы писать — глупо.
                    +1
                    хм… а никто не говорил разве компаниям-разработчикам, что с++ далеко не тривиальный язык и пускать на нем писать можно не всех? :) за код, который Вы приводили в статье, убивать мало) или отпускать программировать на более простых языках — Java, C#, Basic и т.п., где такие ошибки допустить невозможно)
                      +3
                      Когда программный проект делают два разработчика, то они почти всегда профессионалы.

                      Когда в проекте 10-20 разработчиков, то все новички под присмотром старших.

                      Когда в проекте 100 человек, то уровень людей настолько разный, что «пускать на нем писать не всех» просто нереально.
                        0
                        Для того и придумали безопасные языки с низким порогом вхождения.
                        +1
                        Тогда убить придется сразу всех… :)
                        Вы зря считаете, что это удел только плохих программистов и кривых проектов. :) Пара примеров:
                        Проблемы 64-битного кода в реальных программах: FreeBSD
                        Проблемы 64-битного кода в реальных программах: виртуальные функции
                        Проблемы 64-битного кода в реальных программах: qsort
                          +2
                          а кто сказал, что OpenSource свободен от некачественного кода и плохих программистов?)

                          и таки мне хочется надеяться что не всех и толковые программисты существуют :)
                            0
                            Да открытость/закрытость таки же ничего не гарантируют, просто в открытом коде ошибки всем видны и есть надежда, что кто-то да и пришлет по доброте душевной патч, и иногда даже это на практике происходит. Конечно от кривой архитектуры это всё не спасет, но вот пользователи других платформ вполне могут указать на косяки с портируемостью, хотя бы потому, что заинтересованы получить рабочую версию приложения.
                            Ну и немаловажен тот факт, что opensource развивался в среде юниксоидов, а портируемость и переносимость это одна из основ Unix философии. То есть культура писать портируемый код гораздо более развита.
                        0
                        Когда-то, около года назад, мы переносили своё приложение на x64. Программа работала с мультимедиа (видео там, флеш и т.д.). Перенесли. А потом оказалось, что во-первых, ActiveX компонента флеша под 64-битную платформу не существует, а во-вторых, количество видео-кодеков, оптимизированных под x64 — ничтожно. Ну и все, на этом переход закончился. Решили на годик-другой отложить это дело. Рано еще.
                          0
                          То есть как нету? Есть amd64 сборка флеша, разве её не достаточно? И разве семейство кодеков ffmpeg не оптимизировано под amd64? Уже давно сижу практически на чисто 64 битной системе и проблем с декодированием видео не испытываю. Может не там искали?
                            0
                            хех… думается автор вышеизложенного поста таки имел в виду windows, а не gnu/linux :)
                            в свободной системе то понятно давно таких проблем нет, переход на 64 бита уже более-менее состоялся
                              0
                              ну как это нет проблем, сколько лет x64-сборку флеша под линукс ждали? (свободные недоаналоги флеша в расчет не берем)
                                0
                                Дык кодеки то кроссплатформенные, ffmpeg входит в состав cccp, k-lite codec pack, вешаются в систему, как direct show фильтры. С лицензией тоже всё впорядке. LGPL вполне хорошо подходит и для проприетарных приложений.
                                А разве под винду так и не сделали 64 битную сборку флеша? Вроде же они клялись недавно, что теперь у них на всех платформах есть 64битный флеш.
                                  0
                                  нагло врали ;)
                                  насчет кодеков согласен :)
                          +1
                          >>код который работал вчера, неожиданно может перестать работать на следующий день
                          У меня такое случается сплошь и рядом, даже без перехода на х64. А когда найдёшь ошибку, не понимаешь, как с этой ошибкой приложение смогло отработать целый месяц в продакшн.
                            +2
                            хотелось бы еще упомянуть функции с переменным числом аргументов — немало они моей крови попили.
                              +5
                              Я не решился опубликовать здесь монстра — 20 ловушек переноса Си++ — кода на 64-битную платформу. Тем более эта статья 2007 года уже устарела. Мы теперь знаем куда больше ловушек. Недавно мы подготовили курс по разработке 64-битных приложений, где ошибки и способы их диагностики описаны гораздо полнее. Скоро представим публике.
                              +2
                              Спасибо за статью, познавательно.
                                0
                                Боже мой, вы и досюда добрались…
                                Недавно искал информацию по теме — вот эти ваши чуть отличающиеся статьи, неявно рекламирующие Viva64, по всему интернету. На каждом мало мальски популярном форуме вы завели тему и скопировали туда эту статью.

                                Нет, я понимаю, это полезная статья, но не надо же столько спамить!
                                или совсем с продажами плохо?
                                  0
                                  что бы не быть голословным: www.google.ru/search?hl=ru&lr=&newwindow=1&q=64+bit+viva64&start=10&sa=N

                                  Андрей Карпов добрался и до сюда.
                                    0
                                      +1
                                      я не про то, что не могу найти информацию, а про то, что вы ОЧЕНЬ много спаммите повсюду. по мне это не очень хорошая реклама, вообщем-то, неплохого софта.

                                      но знаете, когда фирма дохзодит до того, тчо раздает бумажки у метро — определенный слой населения перестает пользоваться ее услугами. с вашим спамом так же.
                                        0
                                        >> вы ОЧЕНЬ много спаммите повсюду

                                        Вы правда считаете, что лучше было бы не писать ни одной статьи, не упоминать инструмент нигде?

                                        Именно таким способом — абсолютным молчанием — по Вашему продвигаются программные продукты?
                                          0
                                          нет, абсолютное молчание конечно не хорошо.

                                          но не проще было в google adsense правильные ключевые слова назначить?

                                          вот, почитайте пожалуйста: www.google.ru/search?hl=ru&source=hp&q=%D0%BD%D0%B0%D0%B2%D1%8F%D0%B7%D1%87%D0%B8%D0%B2%D0%B0%D1%8F+%D1%80%D0%B5%D0%BA%D0%BB%D0%B0%D0%BC%D0%B0&btnG=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA+%D0%B2+Google&lr=&aq=f&oq=

                                            +2
                                            >> но не проще было в google adsense правильные ключевые слова назначить?

                                            Нет.

                                            У нас есть опыт с рекламой от Google, выводы из которого тянут на отдельную статью, но кратко изложу здесь.

                                            В чем суть adwords? Это когда по ключевым словам в поиске показывается на первой странице рекламный текст. Но если по этим же ключевым словам есть возможность на той же первой странице показывать адекватные собственные статьи, которые реально интересны людям, то это работает ЗНАЧИТЕЛЬНО лучше adwords. Прежде всего потому, что человек прочитав статью запоминает ее. И когда через год-два ему надо будет вспомнить об этой теме, то он вспомнит. А adwords кто запоминает :-).

                                            Так что мы сознательно после экспериментов отказались от adwords в пользу написания хороших (не «копирайтерских») статей по нашей теме.

                                            И тот факт, что Вы нас «узнали», говорит о работоспособности идеи.

                                            Ну а про «навязчивость» — коллега ниже ответил. Мало российских компаний софтовых, вот и выглядит навязчиво.
                                          0
                                          К сожалению, размещение статей сейчас является единственным эффективным методом рассказать разработчикам о том, что мы есть и что мы делаем. Эффективность контекстной рекламы, баннеров, прямого маркетинга в нашем случае крайне низкая. Дорогие виды продвижения нам сейчас недоступны.

                                          Замечу, что на общем фоне мы не так уж выделяемся, если говорить об англоязычном интернете. Дело в том, что почти нет российских компаний, которые выпускают российские продукты в области программирования и пишут об этом на русском языке. В основном все статьи переводные. Вот и кажется, что нас МНОГО везде. ;)
                                      0
                                      >> Недавно искал информацию по теме — вот эти ваши чуть отличающиеся статьи, неявно рекламирующие Viva64.

                                      Иногда и явно :-).

                                      Статьи посвящены реальным проблемам и способам с ними справиться. Упоминание еще и инструмента — это «плата» читателя за то, что он нашел ответ на свой вопрос.

                                      Используйте «доступность» авторов статей и инструмента с пользой. Задайте им интересующий вас (по теме) вопрос, попросите о новой функциональности. Ведь не всегда есть возможность влиять на развитие того или иного инструмента для программистов.
                                        0
                                        А Зря Вы не дали мне тест по ооп написать. Буду помнить
                                      0
                                      Мы готовы и делаем совместные статьи! Если у Вас есть интересные идеи или просто интересные наблюдения, связанные с параллельностью или 64-битностью — пишите нам. Мы обсудим создание совместную статью. Мы живые, мы общаемся, мы прислушиваемся к людям! Мы не абстрактные разработчики и авторы, с которыми нет связи. Мы готовы к общению! Поддержите нас и не ругайте. :) Так сложно быть самостоятельным разработчиком программного обеспечения.
                                        0
                                        Где вы были неделю назад?! ;) Пришлось набить шишки самому, правда там был C++.NET.
                                          0
                                          Эх, а вот выше жалуются на навязчивую рекламу… Недостаточно мы значит работаем, раз Вы про нас не знали и не нашли :-). Будем исправляться.
                                          0
                                          Visual C++ не поддерживает 64-битный встроенный ассемблер. Может быть, это повод отказаться от Visual C++?
                                            0
                                            Сменить компилятор несравнимо сложнее и дороже, чем вынести ассемблерный код в отдельные файлы или переписать его на С++.
                                            0
                                            Че-то как-то все мрачно… А если у меня 200 мб исходников — мне все пересматривать?
                                              0
                                              Для «пересматривания» большого количества исходников есть инструменты, на навязчивую рекламу которых в комментариях выше жутко ругаются ;)
                                                0
                                                Хрен с рекламой, но реально — дает ли это пропиаренненое решение 100% уверенность, что все глюки отловлены? Или все равно глазами смотреть и через тесты гонять?
                                                  0
                                                  100% гарантию может дать только методология математического доказательства корректности программного кода. Этот вариант естественно не рассматриваем. Любой инструмент позволяет обнаружить часть программных ошибок.

                                                  Можно привести аналогию. Задача — выловить всю рыбу в пруду. Ручной метод — удочка. Инструментальный метод — большая сеть. Естественно сеть не даст гарантии, что выловлена вся рыба, но это явно лучше, чем удочка.

                                                  Инструменты статического анализа выявляют в программах те места, которые с большой вероятностью содержат ошибку. Пример кода, на который выдаст предупреждение статический анализатор, встроенный в Visual Studio Team System (ключ /analyze): «if (m_x < 10 && m_x < 10)» — слева и справа от && одинаковые выражения. Это пример диагностики, которая практически всегда выявляет ошибку. В другом случае, диагностика гораздо менее точна. Пример:
                                                  void Foo(char *array, size_t size) {
                                                    for (int i = 0; i < size; ++i)
                                                      array[i] = 'A';
                                                  }

                                                  Имеется здесь 64-битная ошибка или нет, сказать сложно даже человеку. Ему необходимо знать с какими (маленькими или большими) массивами работает функция. Однако анализатор Viva64 предупредит об потенциальный ошибке и человек решит, стоит исправить int на size_t, отключить это диагностическое сообщение или указать в настройках, что программа не обрабатывает массивы более 2 гигабайт (Viva64 не будет считать этот код опасным).

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

                                                  Статический анализ не исключает просмотр человеком исходного кода. Но позволяет существенно сократить объем, необходимый для просмотра. Если прочитать 200 МБайт исходного кода почти нереальная задача (внимание быстро рассеивается и эффективность чтения очень низкая), то используя статический анализ можно просмотреть почти все места, потенциально содержащие 64-битные ошибки за несколько дней. Да, не все места, но почти все.

                                                  Программа ВСЕГДА содержит ошибки. Задача свести какой-то класс ошибок к разумному минимуму. Будь то ошибки GUI, ошибки, приводящие к замедлению или не освобождения памяти. Нет смысла стараться вывести все 64-битные ошибки, оставив тысячи обыкновенных. :) Звучит несколько необычно, но думаю Вы согласитесь. :)

                                                  Вывод – 100% уверенности от использования Viva64 нет. Но очень близко. И еще мы плотно работаем с клиентами и быстро реализуем их пожелания по диагностике и интерфейсу инструмента.
                                                    0
                                                    Насколько я знаю, в вышеприведенном коде, студия все-таки ругнется ворнингом на смешивание знаковых (int) и беззнаковых типов (size_t), и эта ошибка не менее серьезна, чем проблема адресации >INT_MAX в потенциале.
                                                      0
                                                      Можно поменять int на unsigned и говорить об UINT_MAX. Или заменить size_t на ptrdiff_t. По поводу сравнения знакового и беззнакового — да ругнется. Немного неудачно типы в примере выбрал.

                                            Only users with full accounts can post comments. Log in, please.