Диагностическое правило основано на руководстве 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 }
}
Данная диагностика классифицируется как:
|