﻿# V111\. Call of function 'foo' with variable number of arguments\. N argument has memsize type\.


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

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

Рассмотрим первый пример\.

```cpp
const char *invalidFormat = "%u";
size_t value = SIZE_MAX;
printf(invalidFormat, value);
```

Данный код не учитывает, что тип 'size\_t' не эквивалентен типу 'unsigned' на 64\-битной платформе\. Это приведет к выводу на печать некорректного результата, в случае если "value \> UINT\_MAX"\. Анализатор предупреждает вас, что в качестве фактического аргумента используется тип memsize\. А это значит, что вам следует проверить строку 'invalidFormat', задающую формат вывода\. Исправленный вариант может выглядеть, как показано ниже\.

```cpp
const char *validFormat = "%Iu";
size_t value = SIZE_MAX;
printf(validFormat, value);
```

В коде реального приложения эта ошибка может встретиться, например, в следующем виде:

```cpp
wsprintf(szDebugMessage,
          _T("%s location %08x caused an access violation.\r\n"),
         readwrite,
         Exception->m_pAddr);
```

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

```cpp
char buf[9];
sprintf(buf, "%p", pointer);
```

Автор этого неаккуратного кода не учел, что размер указателя в будущем может стать более 32 бит\. В результате данный код на 64\-битной архитектуре приведет к переполнению буфера\. Проанализировав код, на который выдано предупреждение V111, вы можете пойти двумя путями\. Увеличить размер буфера, или переписать код с использованием безопасных конструкций\.

```cpp
char buf[sizeof(pointer) * 2 + 1];
sprintf(buf, "%p", pointer);
// --- or ---
std::stringstream s;
s << pointer;
```

Третий пример\.

```cpp
char buf[9];
sprintf_s(buf, sizeof(buf), "%p", pointer);
```

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

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

```cpp
printf("%d", 10*5);
CString str;
size_t n = sizeof(float);
str.Format(StrFormat, static_cast<int>(n));
```

Диагностируя описанный тип ошибок, к сожалению, часто нет возможности отличить корректный код от не корректного кода\. Данное предупреждение будет выдаваться на многие вызовы функций с переменным количеством аргументов, даже когда вызов совершенно верен\. Это связано с принципиальной опасностью использования таких конструкций языка С\+\+\. Чаще всего проблемы возникают с использованием разновидности следующих функций: 'printf', 'scanf', 'CString::Format'\. Общепринятой практикой является отказ от них и использование безопасных методик программирования\. Мы настоятельно рекомендуем модифицировать код и использовать безопасные методы\. Например, можно заменить 'printf' на 'cout', а 'sprintf' на 'boost::format' или 'std::stringstream'\.

**Примечание\. Борьба с ложными срабатываниями при работе с функциями форматированного вывода**

Диагностика V111 очень проста\. Когда анализатор ничего не знает об эллипсис\-функции, то он предупреждает о всех случаях, когда в функцию передается тип, меняющий свой размер\. Когда информация о функции доступна анализатору, начинает работать более точная диагностика [V576](https://pvs-studio.ru/ru/docs/warnings/v576/), и диагностика V111 не выдается\. Если диагностика V576 отключена, то V111 выдается на всех функциях\.

Соответственно, можно сократить количество ложных срабатываний, предоставив анализатору информацию о функциях форматирования\. Анализатор сам знает о типовых функциях, таких как 'printf', 'sprintf' и так далее, поэтому речь сейчас идёт о разметке функций, реализованных самостоятельно\. Как аннотировать функции рассказывается в описании диагностики [V576](https://pvs-studio.ru/ru/docs/warnings/v576/)\.

Рассмотрим пример\. Пользователь спрашивает: "Почему в случае N1 анализатор не выдает предупреждение V111, а в случае N2 выдаёт?"\.

```cpp
void OurLoggerFn(wchar_t const* const _Format, ...)
{
  ....
}
void Foo(size_t length)
{
  wprintf( L"%Iu", length );     // N1
  OurLoggerFn( L"%Iu", length ); // N2
}
```

Дело в том, что анализатор знает, как работает стандартная функция 'wprintf'\. А вот что такое 'OurLoggerFn' он не знает и на всякий случай предупреждает о передаче memsize\-типа данных \(в данном случае это тип 'size\_t'\) в качестве фактического аргумента в эллипсис функцию\.

Чтобы предупреждение V111 исчезло, следует проаннотировать функцию 'OurLoggerFn' следующим образом:

```cpp
//+V576, function:OurLoggerFn, format_arg:1, ellipsis_arg:2
void OurLoggerFn(wchar_t const* const _Format, ...)
.....
```

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

1. 64\-битные уроки\. Урок 10\. Паттерн 2\. [Функции с переменным количеством аргументов](https://pvs-studio.ru/ru/blog/lessons/0010/)\.