﻿# V641\. Buffer size is not a multiple of element size\.

Анализатор обнаружил потенциальную ошибку, связанную c приведением указателя на исходный буфер к указателю на другой тип, при этом размер исходного буфера некратен размеру одного элемента полученного типа\. Рассмотрим два класса ошибок, допускаемых при приведениях типов\.

Первый класс ошибок связан с выделением некорректного размера памяти для хранения элементов массива с помощью функций 'malloc', 'calloc', 'alloca', 'realloc' и т\.д\.

Как правило, такие ошибки возникают, если размер выделяемой памяти задается константой \(константами в случае 'calloc'\)\. Для правильного выделения памяти под 'N' элементов массива типа 'T' рекомендуется использовать оператор 'sizeof\(T\)'\. В зависимости от функции, аллоцирующей память, конструкция может иметь следующий вид:

```cpp
int *p = (int*)malloc(N * sizeof(int));
```

или

```cpp
int *p = (int*)calloc(N, sizeof(int));
```

В результате неправильного выделения памяти в программе может возникнуть выход за границу массива\.

Рассмотрим пример некорректного кода с использованием функции 'malloc':

```cpp
int *p = (int*)malloc(70);
```

В данном случае будет выделено 70 байт памяти\. Обращение к элементу 'p\[17\]' приведет к неопределенному поведению, поскольку произойдет выход за границу \(необходимо 72 байта для корректного чтения 18\-го элемента\)\. Корректный вариант кода будет следующим:

```cpp
p = (int*)malloc(72);
```

Также возможен случай, когда требуется выделить память для хранения 70 элементов\. Корректным будет такой вариант кода:

```cpp
p = (int*)malloc(70 * sizeof(int));
```

Рассмотрим пример некорректного кода из реального проекта с использованием функции 'calloc':

```cpp
int data16len = MultiByteToWideChar(CP_UTF8, 
                                    0, 
                                    data,
                                    datalen,
                                    NULL,
                                    0);
if (!data16) 
{
  data16 = (wchar_t*)calloc(data16len + 1, 1);
}
MultiByteToWideChar(CP_UTF8, 0, data, -1, data16, data16len);
```

В данном случае хотели создать буфер для сохранения широкой \(wide\) строки после конвертации из UTF\-8 строки\. Однако размер 'wchar\_t' не равен 1 байту \(Windows \- 2 байта, Linux \- 4 байта\)\. Корректный вариант кода будет выглядеть следующим образом:

```cpp
data16 = (wchar_t*)calloc(data16len + 1, sizeof(wchar_t));
```

**Примечание к функции 'calloc'**\. Несмотря на то, что прототип функции выглядит следующим образом:

```cpp
void* calloc(size_t num, size_t size );
```

некоторые программисты полагают, что размер выделяемой памяти равен num\*size и часто меняют аргументы местами\. Такой код может приводить к ошибкам\. Цитата из документации: "Due to the alignment requirements, the number of allocated bytes is not necessarily equal to num\*size\."\.

Второй класс ошибок связан с преобразованием указателя на объект типа 'A' к указателю на объект типа 'B'\. Рассмотрим пример:

```cpp
struct A
{
  int a, b;
  float c;
  unsigned char d;
};

struct B
{
  int a, b;
  float c;
  unsigned short d;
};

....
A obj1;
B *obj2 = (B*)&obj1; //V641
std::cout << obj2->d;
....
```

Как видно из примера, две структуры отличаются друг от друга последним полем\. Поле 'd' у приведённых структур имеет разный размер типа\. При преобразовании указателя 'A\*' к 'B\*' можно получить неопределенное поведение при обращении к полю 'd'\. Отметим, что преобразование указателя 'B\*' к 'A\*' возможно \(хотя это плохой код\), и неопределенного поведения не будет\. 

Анализатор не будет выдавать предупреждения на преобразование указателя 'A\*' к 'B\*', если два класса \(структуры\) находятся в иерархии наследования\.