﻿# V575\. Function receives suspicious argument\.

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

Рассмотрим пример:

```cpp
bool Matrix4::operator==(const Matrix4 &other) const
{
  if (memcmp(this, &other, sizeof(Matrix4) == 0))
    return true;
  ....
}
```

Здесь мы имеем дело с опечаткой\. Круглая скобка поставлена не там, где нужно\. К сожалению, это плохо заметно, и такая ошибка может очень долго присутствовать в коде\. Из\-за опечатки размер сравниваемой памяти вычисляется выражением `sizeof(Matrix4) == 0`\. Так как результат выражение `false`, то сравнивается ноль байт памяти\. Корректный вариант:

```cpp
bool Matrix4::operator==(const Matrix4 &other) const
{
  if (memcmp(this, &other, sizeof(Matrix4)) == 0)
    return true;
  ....
}
```

**Другой пример\.** Диагностическое правило определяет случаи, когда массив, состоящий из элементов `enum`, заполняется с помощью функции `memset`\. При этом подразумевается, что размер элемента не равен одному байту\. Такое заполнение будет некорректным, т\.к\. в этом случае значением заполнится каждый байт, а не каждый элемент массива\.

Некорректный код:

```cpp
enum E { V0, V1, V2, V3, V4 };
E array[123];
memset(array, V1, sizeof(array));
```

Если компилятор сделает размер каждого элемента равным, например, 4 байта, то все элементы массива будут равны значению `0x01010101`, а вовсе не `0x00000001 (V1)`, как ожидает программист\.

Корректный код для заполнения массива:

```cpp
for (size_t i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
{
  array[i] = V1;
}
```

Или:

```cpp
std::fill(begin(array), end(array), V1);
```

**Примечание\. NULL \- странный аргумент\.**

Иногда программисты пытаются вычислить, сколько требуется выделить памяти под буфер, используя код вот такого типа:

```cpp
const char* format = getLocalizedString(id, resource);
int len = ::vsprintf(NULL, format, args);
char* buf = (char*) alloca(len);
::vsprintf(buf, format, args);
```

Учтите, что вызов `::vsprintf(NULL, format, args)` некорректен\. Вот что про это сказано в MSDN:

```cpp
int vsprintf(*buffer, char *format, va_list argptr); 
....
```

> vsprintf and vswprintf return the number of characters written, not including the terminating null character, or a negative value if an output error occurs\. If buffer or format is a null pointer, these functions invoke the invalid parameter handler, as described in Parameter Validation\. If execution is allowed to continue, these functions return \-1 and set errno to EINVAL\.

## Дополнительная настройка

Есть несколько вариантов дополнительной настройки данного диагностического правила\.

### Для нулевых указателей

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

Примером может служить функция `malloc`\. Эта функция может вернуть нулевой указатель\. Соответственно, использование указателя, который вернула функция `malloc`, без предварительной проверки может привести к разыменованию нулевого указателя\.

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

Также может возникнуть желание подсказать анализатору, что определённая функция может вернуть нулевой указатель\.

В этом случае вы можете воспользоваться дополнительными настройками, которые описаны в разделе "[Как указать анализатору, что функция может или не может возвращать nullptr](https://pvs-studio.ru/ru/docs/manual/0040/)"\.

### Для передаваемого диапазона

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

Чтобы разметить параметры как нижнюю и верхнюю границы, необходимо воспользоваться [механизмом пользовательских аннотаций в формате JSON](https://pvs-studio.ru/ru/docs/manual/6810/) и атрибутами `lower_bound` и `upper_bound` соответственно\. Также, если есть параметр, значение которого проверяется на вхождение в диапазон, его нужно разметить как `in_range_value` \(если такого параметра нет, то достаточно разметить только нижнюю и верхнюю границы\)\.

**Пример**\. Есть функция, семантически схожая с [`std::clamp`](https://en.cppreference.com/w/cpp/algorithm/clamp), со следующей сигнатурой:

```cpp
bool foo(int v, int lo, int hi);
```

Эта функция проверяет, что значение первого параметра лежит в диапазоне, переданном в качестве второго \(нижняя граница\) и третьего \(верхняя граница\) параметров\.

Предположим, что есть следующий вызов данной функции:

```cpp
if (foo(x, 100, 0))
{
  ....
}
```

Как мы видим, в вызове перепутаны местами аргументы для нижней и верхней границы\. Тогда, чтобы получить срабатывание в месте вызова функции, необходимо разметить первый параметр функции `foo` как `in_range_value`, второй как `lower_bound`, а третий как `upper_bound`\.

Исправленный код:

```cpp
if (foo(x, 0, 100))
{
  ....
}
```

Примеры разметки параметров функций можно посмотреть [здесь](https://pvs-studio.ru/ru/docs/manual/6743/#examples)\.

**Примечание\. **Данная разметка необходима только для пользовательских функций\.