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

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

Бесплатная лицензия PVS-Studio для специалистов Microsoft MVP
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

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

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

Ваше сообщение отправлено.

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


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

>
>
>
Как стандарт C++0x поможет в борьбе с 6…

Как стандарт C++0x поможет в борьбе с 64-битными ошибками

28 Фев 2010

Программисты видят в стандарте C++0x возможность использовать лямбда-функции и прочие малопонятные для меня сущности :). Я увидел в нем удобные средства, позволяющие исключить многие 64-битные ошибки.

Рассмотрим функцию, которая возвращает true, если хотя бы в одной из строк встречается последовательность "ABC".

typedef vector<string> ArrayOfStrings;
bool Find_Incorrect(const ArrayOfStrings &arrStr)
{
  ArrayOfStrings::const_iterator it;
  for (it = arrStr.begin(); it != arrStr.end(); ++it)
  {
    unsigned n = it->find("ABC");
    if (n != string::npos)
      return true;
  }
  return false;
};

Эта функция корректно ведет себя при компиляции Win32 версии и дает сбой при сборке в режиме Win64. Рассмотрим пример использования функции:

#ifdef IS_64
  const char WinXX[] = "Win64";
#else
  const char WinXX[] = "Win32";
#endif
int _tmain(int argc, _TCHAR* argv[])
{
  ArrayOfStrings array;
  array.push_back(string("123456"));
  array.push_back(string("QWERTY"));
  if (Find_Incorrect(array))
    printf("Find_Incorrect (%s): ERROR!\n", WinXX);
  else
    printf("Find_Incorrect (%s): OK!\n", WinXX);
  return 0;
}
Find_Incorrect (Win32): OK!
Find_Incorrect (Win64): ERROR!

Ошибка заключается в использовании типа unsigned для переменной "n", хотя функция find() возвращает значение типа string::size_type. В 32-битной программе тип string::size_type и unsigned совпадают, и мы получаем верный результат.

В 64-битной программе string::size_type и unsigned перестают совпадать. Так как подстрока не находится, функция find() возвращает значение string::npos, которое равно 0xFFFFFFFFFFFFFFFFui64. Это значение урезается до величины 0xFFFFFFFFu и помещается в 32-битную переменную. В результате условие 0xFFFFFFFFu == 0xFFFFFFFFFFFFFFFFui64 всегда false и мы получаем сообщение "Find_Incorrect (Win64): ERROR!".

Исправим код, используя тип string::size_type.

bool Find_Correct(const ArrayOfStrings &arrStr)
{
  ArrayOfStrings::const_iterator it;
  for (it = arrStr.begin(); it != arrStr.end(); ++it)
  {
    string::size_type n = it->find("ABC");
    if (n != string::npos)
      return true;
  }
  return false;
};

Теперь код работает корректно, хотя постоянно писать тип string::size_type длинно и не красиво. Можно переопределить его через typedef, но все равно это выглядит как-то сложно. Используя C++0x, мы можем сделать код гораздо изящней надежней.

Для этого мы воспользуемся ключевым словом auto. Ранее auto означало, что переменная создается на стеке, и подразумевалось неявно в случае, если вы не указали что-либо другое, например register. Теперь тип переменной, объявленной как auto, определяется компилятором самостоятельно на основе того, чем эта переменная инициализируется.

Следует заметить, что auto-переменная не сможет хранить значения разных типов в течение одного запуска программы. Си++ остается статически типизированным языком, и указание auto лишь говорит компилятору самостоятельно позаботиться об определении типа: после инициализации сменить тип переменной будет уже нельзя.

Воспользуемся ключевым словом auto в нашем коде. Проект был создан в Visual Studio 2005, а поддержка C++0x реализована только начиная с версии Visual Studio 2010. Поэтому для компиляции я воспользовался компилятором Intel C++, входящим в состав Intel Parallel Studio 11.1 и поддерживающим стандарт C++0x. Поддержка C++0x включается в разделе Language и носит название "Enable C++0x Support". Как видно из рисунка 1, эта опция является Intel Specific.

0060_In_what_way_can_C++0x_standard_help_you_eliminate_64-bit_errors_ru/image1.png

Рисунок 1 - Поддержка стандарта C++0x

Модифицированный код выглядит следующим образом:

bool Find_Cpp0X(const ArrayOfStrings &arrStr)
{
  for (auto it = arrStr.begin(); it != arrStr.end(); ++it)
  {
    auto n = it->find("ABC");
    if (n != string::npos)
      return true;
  }
  return false;
};

Обратите внимание, как теперь объявлена переменная "n". Неправда ли, элегантно и устраняет от ряд ошибок, в том числе и 64-битных. Переменная "n" будет иметь ровно тот тип, который возвращает функция find(), то-есть string::size_type. Также обратите внимание, что исчезла строчка объявления итератора:

ArrayOfStrings::const_iterator it;

Объявлять переменную it внутри цикла было некрасиво (длинно). И поэтому объявление было вынесено отдельно. Теперь проблемы длины нет:

for (auto it = arrStr.begin(); ......)

Рассмотрим еще одно ключевое слово - decltype. Оно позволяет задать тип на основании типа другой переменной. Если бы в нашем коде, мы должны были объявить все переменные заранее, то можно было бы написать так:

bool Find_Cpp0X_2(const ArrayOfStrings &arrStr)
{
  decltype(arrStr.begin()) it;
  decltype(it->find("")) n;
  for (it = arrStr.begin(); it != arrStr.end(); ++it)
  {
    n = it->find("ABC");
    if (n != string::npos)
      return true;
  }
  return false;
};

Конечно, в данном случае это бессмысленно, но может быть весьма удобно во многих ситуациях.

К сожалению (или к счастью для нас :-), хотя новый стандарт облегчает написание безопасного 64-битного кода, он не помогает в устранении уже существующих дефектов. Для того чтобы использовать memsize-тип или auto для устранений ошибки, эту ошибку нужно в начале обнаружить. Следовательно, актуальность использования инструмента Viva64 с приходом стандарта C++0x не изменится.

P.S.

Проект с кодом можно скачать здесь.

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

Дата: 30 Янв 2019

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

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

Дата: 19 Май 2017

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

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

Дата: 22 Окт 2018

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

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

Дата: 16 Окт 2017

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

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

Дата: 27 Июн 2017

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

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

Дата: 21 Ноя 2018

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

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

Дата: 22 Дек 2018

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

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

Дата: 14 Апр 2016

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

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

Дата: 20 Мар 2017

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

В своей предыдущей статье я писал, что мне не нравится подход, при котором статические анализаторы кода оцениваются с помощью синтетических тестов. В статье приводился пример, воспринимаемый анализат…
PVS-Studio для Java

Дата: 17 Янв 2019

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

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

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

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