﻿# V108\. Incorrect index type: 'foo\[not a memsize\-type\]'\. Use memsize type instead\.


> Это правило входит в группу "Диагностика 64\\\-битных ошибок"\\\. Разработка правил этой группы больше не ведётся, и в будущем они могут быть отключены\\\. Если вы используете эти правила, \[свяжитесь с нашей поддержкой\]\(https://pvs\-studio\.ru/ru/about\-feedback/\) — мы поможем найти замену или предложим альтернативное решение\\\.

Анализатор обнаружил потенциально возможную ошибку индексации больших массивов\. Ошибка может заключаться в некорректном вычислении индекса\. 

Первый пример\.

```cpp
extern char *longString;
extern bool *isAlnum;
...
unsigned i = 0;
while (*longString) {
  isAlnum[i] = isalnum(*longString++);
  ++i;
}
```

Данный код совершенно корректен для 32\-битной платформы, где принципиально невозможна обработка массивов более 'UINT\_MAX' байт \(4 Гб\)\. На 64\-битной платформе можно обработать массив размером более 4 Гб, что иногда очень удобно\. Но корректно обработать большой массив приведенным алгоритмом невозможно\. Ошибка заключается в использовании для индексации массива 'isAlnum' переменной типа 'unsigned'\. Когда мы заполним первые 'UINT\_MAX' элементов, произойдет переполнение переменной 'i', и она приравняется нулю\. В результате мы начнем перезаписывать элементы массива 'isAlnum' расположенные в начале, а часть элементов оставим неинициализированными\.

Корректным исправлением является изменения типа переменной 'i' на [memsize](https://pvs-studio.ru/ru/blog/terms/0030/) тип:

```cpp
...
size_t i = 0;
while (*longString)
  isAlnum[i++] = isalnum(*longString++);
```

Второй пример\.

```cpp
class Region {
  float *array;
  int Width, Height, Depth;
  float Region::GetCell(int x, int y, int z) const;
  ...
};
float Region::GetCell(int x, int y, int z) const {
  return array[x + y * Width + z * Width * Height];
}
```

Для программ численного моделирования важным ресурсом является объем оперативной памяти, и возможность на 64\-битной архитектуре использовать более 4 Гб памяти существенно увеличивает вычислительные возможности\. В таких программах часто используют одномерные массивы, работая затем с ними как с трехмерными\. Для этого существуют функции, аналогичные 'GetCell', обеспечивающие доступ к необходимым элементам счетной области\. Но приведенный код может корректно работать с массивами, содержащими не более 'INT\_MAX' \(2 Гб\) элементов\. Причина заключается в использовании 32\-битных типов 'int', участвующих в вычислении индекса элемента\. Если количество элементов в массиве 'array' превысит 'INT\_MAX' \(2 Гб\), то произойдет переполнение и значение индекса будет вычислено некорректно\. Программисты часто допускают ошибку, пытаясь исправить код следующим образом:

```cpp
float Region::GetCell(int x, int y, int z) const {
  return array[static_cast<ptrdiff_t>(x) + y * Width +
                    z * Width * Height];
}
```

Они знают, что по правилам языка Си\+\+ выражение для вычисления индекса будет иметь тип 'ptrdiff\_t' и надеются за счет этого избежать переполнения\. К сожалению, переполнение может произойти внутри подвыражения "y \* Width или z \* Width \* Height" ,так как для их вычисления по\-прежнему используется тип 'int'\.

Если вы хотите исправить код, не изменяя типов переменных, участвующих в выражении, то вы должны [явно привести](https://pvs-studio.ru/ru/blog/terms/0015/) каждую переменную к memsize типу:

```cpp
float Region::GetCell(int x, int y, int z) const {
  return array[ptrdiff_t(x) +
 ptrdiff_t(y) * ptrdiff_t(Width) +
 ptrdiff_t(z) * ptrdiff_t(Width) *
 ptrdiff_t(Height)];
}
```

Другое решение \- изменить типы переменных на memsize тип:

```cpp
class Region {
  float *array;
  ptrdiff_t Width, Height, Depth;
  float
    Region::GetCell(ptrdiff_t x, ptrdiff_t y, ptrdiff_t z) const;
  ...
};
float Region::GetCell(ptrdiff_t x, ptrdiff_t y, ptrdiff_t z) const
{
  return array[x + y * Width + z * Width * Height];
}
```

Если вы используете для индексации выражения, имеющие тип отличный от memsize, но при этом точно уверены в корректности кода, то для подавления диагностирующих сообщений анализатора можно использовать явное приведение типов, как показано ниже\.

```cpp
bool *Seconds;
int min, sec;
...
bool flag = Seconds[static_cast<size_t>(min * 60 + sec)];
```

Если вы подозреваете в программе наличие ошибок, связанных с некорректным явным приведением типов в выражениях, то вы можете воспользоваться предупреждением [V201](https://pvs-studio.ru/ru/docs/warnings/v201/)\.

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

```cpp
char Arr[] = { '0', '1', '2', '3', '4' };
char *p = Arr + 2;
cout << p[0u + 1] << endl;
cout << p[0u - 1] << endl; //V108
```

Данный код исправно работает в 32\-битном режиме и печатает на экране числа 3 и 1\. При проверке этого кода мы получим предупреждение только на одну  строку с выражением "p\[0u \- 1\]"\. И это совершенно верно\. Если вы скомпилируете и запустите данный пример в 64\-битном режиме, то увидите, как на экране будет распечатано значение 3, после чего произойдет аварийное завершений программы\.

Ошибка связана с тем, что индексация "p\[0u \- 1\]" на 64\-битной системе некорректна, о чем и предупреждает анализатор\. Согласно правилам языка Си\+\+ выражение "0u \- 1" будет иметь тип unsigned и равняться 0xFFFFFFFFu\. На 32\-битной архитектуре сложение указателя с этим числом будет эквивалентно вычитанию единицы\. А на 64\-битной системе к указателю будет честно прибавлено 0xFFFFFFFFu и произойдет обращение к памяти за приделами массива\.

Конечно, часто индексация к массивам с использованием таких типов как int и unsigned безопасна\. В этом случае предупреждения анализатора могут казаться не уместными\. Но следует учитывать, что такой код все равно может оказаться ненадежным в случае его модернизации для обработки другого набора данных\. Код с использованием типов int и unsigned может оказаться менее производительным, чем это возможно на 64\-битной архитектуре\.

Если вы уверены в корректности индексации, то вы можете воспользоваться функцией "[Suppression of false alarms](https://pvs-studio.ru/ru/docs/manual/0017/)" или использовать фильтры\. Можно использовать явное приведение типов в коде:

```cpp
for (int i = 0; i != n; ++i)
  Array[static_cast<ptrdiff_t>(i)] = 0;
```

Дополнительные материалы по данной теме:

1. 64\-битные уроки\. Урок 13\. Паттерн 5\. [Адресная арифметика](https://pvs-studio.ru/ru/blog/lessons/0013/)\.