Данное диагностическое правило основано на руководстве MISRA (Motor Industry Software Reliability Association) по разработке программного обеспечения.
Это правило актуально только для C. В зависимости от версии стандарта языка C, битовые поля должны быть объявлены только с подходящими для этого типами. Для C90: 'signed int' или 'unsigned int'. Для C99: 'signed int', 'unsigned int', '_Bool' или другой интегральный тип, разрешённый имплементацией, с явным указанием модификатора 'signed' или 'unsigned'.
Также разрешается использовать псевдоним ('typedef') для допустимого типа.
Битовое поле типа 'int' может быть как 'signed', так и 'unsigned' в зависимости от компилятора. Если для представления битового поля используется 'unsigned int', то все выделенные для поля биты будут значимыми. Такое битовое поле из 'n' битов имеет диапазон значений '[0, 2 ^ n - 1]'.
Если же для представления битового поля используется 'signed int', то 1 бит будет выделен как знаковый. Поэтому для записи значимой части значения битового поля будет использовано на 1 бит меньше выделенного. Такое битовое поле из 'n' битов имеет диапазон значений '[-2 ^ (n - 1), 2 ^ (n - 1) - 1]'.
Исходя из этого, в зависимости от компилятора битовые поля типа 'int' могут иметь разные диапазоны значений. Чтобы избежать ошибок, которые могут возникнуть из-за этого, нужно явно указывать модификатор знаковости.
Пример неправильного использования битового поля:
struct S
{
int b : 3; // <=
};
void foo()
{
S s;
s.b = 5;
if (s.b != 5)
{
Boom();
}
}
В этом примере, если компилятор выберет беззнаковый тип для представления битового поля 'b', тогда все 3 бита, в которые записывается значение 5, будут значимыми. То есть фактически записывается остаток по модулю 8, и код будет работать как ожидается – в битовое поле будет записано значение 5.
Если компилятор выберет знаковый тип для представления битового поля 'b', то оно разбивается на 1 бит знака и 2 бита значимой части. При записи числа 5 в 'b' в значимую часть запишутся только 2 младших бита. В результате в битовое поле будет записано значение 1 вместо значения 5. Поэтому проверка будет пройдена, и будет вызвана функция 'Boom'.
Для исправления нужно явно указывать модификатор знаковости ('signed' / 'unsigned'):
struct S
{
unsigned int b : 3;
};
void foo()
{
S s;
s.b = 5;
if (s.b != 5)
{
Boom();
}
}
Для того, чтобы явно указать, какие типы допускаются в объявлении битовых полей вашим компилятором, поддерживающим стандарт C99, в файл с исходными кодом или pvsconfig-файл можно добавить следующий комментарий:
//V_2591_ALLOWED_C99_TYPES:short,long long
После двоеточия через запятую указываются допустимые типы без модификаторов знаковости:
Данная диагностика классифицируется как:
|