Мы используем куки, чтобы пользоваться сайтом было удобно.
Хорошо
to the top

Вебинар: Инструменты для разработчиков игр и не только - 26.02

>
>
>
V2668. MISRA. The macro parameter...
menu mobile close menu
Проверка проектов
Дополнительная информация
toggle menu Оглавление

V2668. MISRA. The macro parameter immediately following a '#' operator should not immediately be followed by a '##' operator.

05 Фев 2026

Диагностическое правило основано на руководстве MISRA (Motor Industry Software Reliability Association) по разработке программного обеспечения.

Правило актуально только для С.

За параметром макроса, предваряемым оператором #, не должен сразу следовать оператор ##. Использование такой подстановки в функциональном макросе может привести к формированию некорректного токена препроцессирования и неопределённому поведению.

Операторы # и ## останавливают раскрытие своих операндов, которые определены в списке подстановки. Результат выполнения оператора # — строковой литерал, результат выполнения оператора ## — склеенные левый и правый операнды. Маловероятно, что в результате конкатенации строкового литерала с токеном препроцессирования будет получен другой валидный токен.

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

#include <stdio.h>

#define my_print(a, b) printf("%s", #a ## b)

#define HELLO "hello"
#define WORLD "world"
#define HELLOWORLD "hello, world"

int main(int argc, char *argv[])
{
  my_print(HELLO, WORLD);
  return 0;
}

В результате конкатенации токенов в макросе my_print будет получен токен "HELLO"WORLD, который передаётся как аргумент функции printf. Этот токен не является валидным, и поведение в этой ситуации не определено. Компиляторы GCC и Clang не компилируют такой код, в то же время MSVC успешно компилирует и выводит HELLOworld.

Вероятно, программист хотел склеить имена макросов HELLO и WORLD для получения токена HELLOWORLD, который затем будет раскрыт препроцессором в строковый литерал "hello, world":

#include <stdio.h>

#define my_print(a, b) printf("%s", a ## b)

#define HELLO "hello"
#define WORLD "world"
#define HELLOWORLD "hello, world"

int main(int argc, char *argv[])
{
  my_print(HELLO, WORLD);
  return 0;
}

Теперь препроцессор склеит HELLO и WORLD в HELLOWORLD, затем он будет раскрыт в строковый литерал "hello, world" при повторном сканировании и передан в функцию printf в качестве аргумента.

Примечание. Допускается использование последовательности операторов ## и # в обратном порядке, например, такой макрос может быть использован для образования wide string literals:

#define GET_WIDE_STR(a, b) a ## #b

int main(int argc, char *argv[])
{
  GET_WIDE_STR(L, raw_string_literal);
  return 0;
}

/* Results in:

int main(int argc, char *argv[])
{
  L"raw_string_literal";
  return 0;
}

*/

Данная диагностика классифицируется как:

  • MISRA-C-2012-20.11
  • MISRA-C-2023-20.11