Для получения триального ключа
заполните форму ниже
Team License (базовая версия)
Enterprise License (расширенная версия)
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

** На сайте установлена reCAPTCHA и применяются
Политика конфиденциальности и Условия использования Google.
Запросите информацию о ценах
Новая лицензия
Продление лицензии
--Выберите валюту--
USD
EUR
GBP
RUB
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

** На сайте установлена reCAPTCHA и применяются
Политика конфиденциальности и Условия использования Google.
Для получения лицензии для вашего открытого
проекта заполните, пожалуйста, эту форму
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

** На сайте установлена reCAPTCHA и применяются
Политика конфиденциальности и Условия использования Google.
Для получения лицензии для вашего открытого
проекта заполните, пожалуйста, эту форму
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

** На сайте установлена reCAPTCHA и применяются
Политика конфиденциальности и Условия использования Google.
Мне интересно попробовать плагин на:
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

** На сайте установлена reCAPTCHA и применяются
Политика конфиденциальности и Условия использования Google.
Ваше сообщение отправлено.

Мы ответим вам на


Если вы так и не получили ответ, пожалуйста, проверьте папку
Spam/Junk и нажмите на письме кнопку "Не спам".
Так Вы не пропустите ответы от нашей команды.

>
>
>
Особенности разработки 64-битных прилож…

Особенности разработки 64-битных приложений

10 Ноя 2006

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

Введение

Традиционные 32-битные приложения уже начинают достигать предела своих возможностей по использованию оперативной памяти. В Windows-системах для пользовательского приложения доступно два гигабайта памяти (в некоторых случаях три), в то время как для повышения производительности программ в оперативной памяти уже необходимо хранить больше данных. Так, ограничения на память заставляют любителей компьютерных игр все чаще и чаще ждать дозагрузки частей одного уровня, что значительно снижает "эффект присутствия". Пользователи, обрабатывающие видеоизображения, вынуждены обрабатывать кадры с жесткого диска, вместо того, чтобы хранить все данные в быстрой оперативной памяти. Наконец, ученые для своей научной работы (моделирование, визуализация) должны ограничиваться областями минимального размера. Ведь моделировать области большой размерности со скоростью, адекватной задаче, можно лишь храня данные в оперативной памяти машины. И это уже не говоря о задачах, для решения которых необходимо использовать базы данных!

Наметившийся в конце прошлого века кризис в программном мире необходимо было как-то разрешить. Из истории человечества известны два способа развития: эволюционный и революционный. Разумеется, все ждут революции, которая позволит программистам не заботиться о размере оперативной памяти, скорости расчета и других вещах, игнорирование которых приводит к программам-монстрам. Однако, дата следующей компьютерной революции пока неизвестна (во всяком случае, автору данной статьи), а проблему решать надо уже сегодня (точнее даже "вчера"). Властелины компьютерного мира в лице компаний AMD и Intel предложили эволюционное увеличение разрядности машины. Вместо 32-битной архитектуры нам предложили 64-битную. Другими словами, для адресации ячейки оперативной памяти используются не 32-битные числа, а 64-битные. Это приводит к увеличению количества максимально доступной памяти до немыслимых пока значений. Такой вариант развития не является новым в компьютерном мире. Программисты постарше застали переход от 16-битного программного обеспечения к 32-битному, который начался при появлении процессора Intel 80386. Инженеры AMD и Intel решили повторить успех прошлого, увеличив адресное пространство и количество регистров процессора. В результате, проблемы современных компьютеров хоть и не были решены принципиально, но, в тоже время, необходимость их срочного решения была отложена.

64 бита для программистов: укрощение программ

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

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

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

Обратимся к конкретным примерам, которые нельзя найти в официальной документации к средствам разработки. Для хранения размеров блоков памяти, количества элементов массива и других подобных вещей в языке C++ используется специальный тип данных size_t. Размер этого типа совпадает с разрядностью процессора, т.е. на 32-битных системах его размер составляет 4 байта, а на 64-битных - 8 байт. Соответственно, на 32-битных системах максимально мы можем получить (теоретически) блок памяти размером в четыре миллиарда ячеек, а на 64-битных системах - значительно больший блок памяти. Казалось бы, программа автоматически получает преимущества 64-битных приложений сразу же после перекомпиляции. Но дьявол кроется в деталях. Везде ли в Ваших программах при работе с большими массивами и блоками памяти Вы используете тип size_t? Не было ли такого, что при написании кода для 32-битных систем, Вы рассуждали так: "Этот блок памяти уж точно никогда не будет больше гигабайта"? Если такое было, то вполне возможно, что для хранения размера блока памяти Вы использовали переменную типа int. Но ведь размер этой переменной даже на 64-битных системах равен 4 байтам! В результате, несмотря на то, что на 64-битной системе Вы могли бы выделить любое количество памяти для этого блока, фактически же Вы будете ограничены 4 гигабайтами. Это происходит из-за неверно выбранного типа переменной, в которой хранится размер блока памяти.

Но предположим, в Вашей программе размеры блоков памяти вычисляются верно. В таком случае выделяться память будет действительно большого размера, однако приложение все равно может оказаться неработоспособным. Почему такое может быть, если для хранения количества элементов массива мы используем переменную типа size_t? Давайте посмотрим на простейший цикл, в котором массив из 5 миллиардов элементов заполняется числами от 1 до 5000000000:

  size_t maxSize = 5000000000;
  int *buffer = new int[maxSize];
  size_t count = 0;
  for (int i = 0; i < maxSize; ++i) {
    buffer[i] = i;
  }     
  // ...
  delete[] buffer;

Если бы массив имел размер не 5 миллиардов, а 5 миллионов элементов, то данный код был бы корректен и на 32-битной, и на 64-битной системе. Однако 5 миллиардов элементов на 32-битной системе в оперативной памяти не поместятся. Но у нас же 64-битная система, поэтому для нас это не проблема, не так ли? Не так! В данном фрагменте переменная maxSize на 64-битной системе является 64-битной. Но счетчик цикла i (int) так и остался 32-битным. В результате значение переменной i будет изменяться от 0 до ... -2147483648 (минус два миллиарда)! Такой неожиданный эффект происходит из-за переполнения переменной. Будет ли при этом корректно заполнен наш массив? Вместо теоретических рассуждений о представлении данных в компьютере проведем эксперимент. Изменим код так:

size_t maxSize = 5000000000;
size_t count = 0;
for (int i = 0; i < maxSize; ++i) {
  count++;
}

После завершения цикла посмотрим на значение переменной count. Оно окажется равным... 2147483648. Вместо 5 миллиардов раз наш цикл выполнился всего лишь 2 миллиарда. В случае заполнения массива в цикле более чем половина элементов останутся неинициализированными!

В чем же проблема с такими конструкциями? Дело в том, что компиляторы не выдают диагностических сообщений на подобный код, поскольку с точки зрения С++ он написан корректно: переменная i преобразуется к типу size_t. Но мы-то хотели другого поведения программы. Помочь в диагностике подобных ошибок могут статические анализаторы кода, ориентированные на поиск проблем, связанных с переносом на 64-битные системы.

С приведением типов переменных связаны и другие проблемы. Предположим, что существует функция, принимающая аргумент переменной размерности size_t, которая рассчитывает некий коэффициент:

int Calc(size_t size) {
  // ...
}

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

Есть и более интересные ошибки в коде, которые могут привести к неожиданному поведению в программах, перенесенных из 32-битной среды в 64-битную. Например, в приложении может сломаться подсистема справки. Как связана справка с 64-битным кодом? Никак. Но вот с какой ситуацией пришлось столкнуться автору. Обычное Windows-приложение было написано на Visual C++ с использованием библиотеки MFC. Эта библиотека заслуженно пользуется уважением у разработчиков, так как позволяет легко создать каркас приложения без лишних трудностей, в том числе и добавить поддержку справочной системы. Для этого нужно всего лишь в классе приложения перекрыть виртуальную функцию WinHelp(). При этом иерархия наследования в Visual C++ 6.0 выглядела примерно так:

class CWinApp {
  virtual void WinHelp(DWORD dwData, UINT nCmd);
};
class CMyApp : public CWinApp {
  virtual void WinHelp(DWORD dwData, UINT nCmd);
};

В последующих версиях Visual C++ для поддержки 64-битного кода аргумент функции WinHelp() в библиотеке MFC был изменен с типа DWORD на тип DWORD_PTR:

class CWinApp {
  virtual void WinHelp(DWORD_PTR dwData, UINT nCmd);
}

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

 class CMyApp : public CWinApp {
  virtual void WinHelp(DWORD_PTR dwData, UINT nCmd);
};

После чего справочная система заработала снова.

Заключение

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

  • они появляются при переносе старых или разработке новых приложений на 64-битную платформу;
  • большинство из них не диагностируются компилятором, так как с точки зрения языка С++ код выглядит корректно;
  • подобные проблемы могут сильно снизить впечатление от 64-битной версии Вашего приложения.

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

Популярные статьи по теме
PVS-Studio для Java

Дата: 17 Янв 2019

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

В седьмой версии статического анализатора PVS-Studio мы добавили поддержку языка Java. Пришло время немного рассказать, как мы начинали делать поддержку языка Java, что у нас получилось и какие дальн…
Любите статический анализ кода!

Дата: 16 Окт 2017

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

Я в шоке от возможностей статического анализа кода, хотя сам участвую в разработке инструмента PVS-Studio. На днях я был искренне удивлён тому, что анализатор оказался умнее и внимательнее меня.
Технологии, используемые в анализаторе кода PVS-Studio для поиска ошибок и потенциальных уязвимостей

Дата: 21 Ноя 2018

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

Краткое описание технологий, используемых в инструменте PVS-Studio, которые позволяют эффективно обнаруживать большое количество паттернов ошибок и потенциальных уязвимостей. Статья описывает реализа…
Характеристики анализатора PVS-Studio на примере EFL Core Libraries, 10-15% ложных срабатываний

Дата: 31 Июл 2017

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

После большой статьи про проверку операционной системы Tizen мне было задано много вопросов о проценте ложных срабатываний и о плотности ошибок (сколько ошибок PVS-Studio выявляет на 1000 строк кода)…
Как PVS-Studio оказался внимательнее, чем три с половиной программиста

Дата: 22 Окт 2018

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

PVS-Studio, как и другие статические анализаторы кода, часто выдаёт ложные срабатывания. Но не стоит спешить считать странные срабатывания ложными. Это короткая история о том, как PVS-Studio вновь ок…
Зло живёт в функциях сравнения

Дата: 19 Май 2017

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

Возможно, читатели помнят мою статью под названием "Эффект последней строки". В ней идёт речь о замеченной мной закономерности: ошибка чаще всего допускается в последней строке однотипных блоков текс…
PVS-Studio ROI

Дата: 30 Янв 2019

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

Время от времени нам задают вопрос, какую пользу в денежном эквиваленте получит компания от использования анализатора PVS-Studio. Мы решили оформить ответ в виде статьи и привести таблицы, которые по…
Статический анализ как часть процесса разработки Unreal Engine

Дата: 27 Июн 2017

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

Проект Unreal Engine развивается - добавляется новый код и изменятся уже написанный. Неизбежное следствие развития проекта - появление в коде новых ошибок, которые желательно выявлять как можно раньш…
Бесплатный PVS-Studio для тех, кто развивает открытые проекты

Дата: 22 Дек 2018

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

В канун празднования нового 2019 года команда PVS-Studio решила сделать приятный подарок всем контрибьюторам open-source проектов, хостящихся на GitHub, GitLab или Bitbucket. Им предоставляется возмо…
Главный вопрос программирования, рефакторинга и всего такого

Дата: 14 Апр 2016

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

Вы угадали, ответ - "42". Здесь приводится 42 рекомендации по программированию, которые помогут избежать множества ошибок, сэкономить время и нервы. Автором рекомендаций выступает Андрей Карпов - тех…

Комментарии (0)

Следующие комментарии

На сайте установлена reCAPTCHA и применяются
Политика конфиденциальности и Условия использования Google.
Этот сайт использует куки и другие технологии, чтобы предоставить вам более персонализированный опыт. Продолжая просмотр страниц нашего веб-сайта, вы принимаете условия использования этих файлов. Если вы не хотите, чтобы ваши данные обрабатывались, пожалуйста, покиньте данный сайт. Подробнее →
Принять