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

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

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

```cpp
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](https://pvs-studio.ru/ru/blog/terms/0054/) версии и дает сбой при сборке в режиме [Win64](https://pvs-studio.ru/ru/blog/terms/0055/)\. Рассмотрим пример использования функции:

```cpp
#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\.

```cpp
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/image2.png](https://import.viva64.com/docx/blog/0060_In_what_way_can_C++0x_standard_help_you_eliminate_64-bit_errors_ru/image2.png)

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

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

```cpp
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\. Также обратите внимание, что исчезла строчка объявления итератора:

```cpp
ArrayOfStrings::const_iterator it;
```

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

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

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

```cpp
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\-тип](https://pvs-studio.ru/ru/blog/terms/0030/) или auto для устранений ошибки, эту ошибку нужно в начале обнаружить\. Следовательно, актуальность использования инструмента [Viva64](https://pvs-studio.ru/ru/pvs-studio/) с приходом стандарта C\+\+0x не изменится\.

P\.S\.

 Проект с кодом можно скачать [здесь](https://pvs-studio.com/external-pictures/newcpptest.7z)\.