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

Диагностическое правило основано на руководстве [MISRA](https://misra.org.uk/) \(Motor Industry Software Reliability Association\) по разработке программного обеспечения\.

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

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

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

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

```cpp
#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 [не компилируют](https://godbolt.org/z/9Y85odzGq) такой код, в то же время MSVC успешно [компилирует и выводит](https://godbolt.org/z/sGGnKP78d) `HELLOworld`\.

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

```cpp
#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](https://en.cppreference.com/w/c/language/string_literal.html):

```cpp
#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;
}

*/
```