V2651. MISRA. Initializer using chained designators should not contain initializers without designators.
Диагностическое правило основано на руководстве MISRA (Motor Industry Software Reliability Association) по разработке программного обеспечения.
Правило актуально только для языка C.
Не следует комбинировать вложенные десигнаторы и позиционную инициализацию в пределах агрегатной инициализации объекта. Это может ухудшить чтение кода или привести к неожиданным проблемам.
При декларации объектов структур, объединений или массивов программист может предоставить начальные значения полей или элементов при помощи агрегатной инициализации ({ .... }). Инициализаторы могут быть двух видов: позиционные и назначенные.
Позиционные инициализаторы заполняют:
- элементы массива в порядке увеличения индекса элемента;
- поля структуры в порядке их объявления.
Все элементы массива или поля структур, к которым отсутствует явный инициализатор, будут заполнены нулями. Пример позиционной инициализации:
typedef struct
{
int x;
int y;
int z;
} TypeA;
TypeA obj = { 0, 0 }; // positional initialization
// obj == { 0, 0, 0 }
Назначенные инициализаторы (десигнаторы) позволяют заполнить указанный элемент массива или поле структуры. Пример назначенной инициализации:
typedef struct
{
int x;
int y;
int z;
} TypeA;
TypeA obj = { .x = 1, .y = 2 }; // designated initialization
// ^~ ^~ // obj == { 1, 2, 0 }
// \_____/
// designators
Если инициализатор, следующий после десигнатора, использует позиционную форму, то будет заполнен элемент массива или поле структуры, следующие за тем, что указан в десигнаторе. Пример:
typedef struct
{
int x;
int y;
int z;
} TypeA;
TypeA obj = { .y = 2, 3 }; // mixed aggregate initialization
// obj == { 0, 2, 3 }
Если десигнатор состоит из составного имени, то он считается вложенным (nested/chained) и позволяет инициализировать подобъект внутри поля структуры или элемента массива:
typedef struct
{
int x;
int y;
} TypeA;
typedef struct
{
TypeA a;
double coeff;
} TypeB;
TypeB obj = { .a.x = -1, .coeff = 0.5 }; // obj == { { -1, 0 }, 0.5 }
// ^~~~
// \
// nested/chained designator
Для улучшения читаемости кода и во избежание неожиданных проблем не рекомендуется использовать вложенные десигнаторы и позиционные инициализаторы в пределах одного уровня инициализаторов. Рассмотрим ошибочный пример:
typedef struct
{
int x;
int y;
} Point;
typedef struct
{
Point center;
double radius;
int fill_color;
} Circle;
static Circle CreateRedCircle(void)
{
Circle res = { .center.x = 5,
1.0,
0xFF0000 };
return res; // Expects res == { { 5, 0 }, 1.0, 0xFF0000 }
// Actual res == { { 5, (int) 1.0 },
// (double) 0xFF0000,
// 0 }
}
Функция CreateRedCircle используется для создания красной окружности с центром (5; 0), радиусом 1.0 и заливкой красного цвета. Однако из-за свойств десигнаторов, описанных выше, объект будет инициализирован следующим образом:
- инициализатор
1.0будет применён к полю.center.y, а не к.radius; - инициализатор
0xFF0000будет применён к полю.radius, а не к.fill_color; - поле
.fill_colorбудет инициализировано нулём.
Для исправления проблемы следует использовать десигнаторы для каждого поля объекта и его подобъекта:
static Circle CreateRedCircle(void)
{
Circle res = { .center.x = 5,
.center.y = 0,
.radius = 1.0,
.fill_color = 0xFF0000 };
return res; // Expects res == { { 5, 0 }, 1.0, 0xFF0000 }
// Actual res == { { 5, 0 }, 1.0, 0xFF0000 }
}
Исключение. Инициализатор подобъекта в своём списке инициализации может опускать десигнаторы для указания элементов, если он не содержит никаких вложенных десигнаторов, и ни один из вложенных десигнаторов в обрамляющем списке инициализации не ссылается на элемент внутри этого подобъекта:
static Circle CreateRedCircle(void)
{
Circle res = { .center = { 5, 0 },
.radius = 1.0,
.fill_color = 0xFF0000 };
return res; // Expects res == { { 5, 0 }, 1.0, 0xFF0000 }
// Actual res == { { 5, 0 }, 1.0, 0xFF0000 }
}
Данная диагностика классифицируется как:
|