V2668. MISRA. The macro parameter immediately following a '#' operator should not immediately be followed by a '##' operator.
Диагностическое правило основано на руководстве 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;
}
*/
Данная диагностика классифицируется как:
|