Использование типов фиксированной ширины не гарантирует защиту от неопределенного поведения , связанного со сдвигом битов. Раздел 7.20.1.1 C стандарта относительно точных состояний целочисленных типов ширины:
1 Имя typedef intN_t
обозначает целочисленный тип со знаком с шириной N, без дополнительных битов и представления дополнения до двух. Таким образом, int8_t
обозначает такой целочисленный тип со знаком с шириной ровно 8 бит.
2 Имя typedef uintN_t
обозначает целочисленный тип без знака с шириной N и без битов заполнения. , Таким образом, uint24_t
обозначает такой целочисленный тип без знака с шириной ровно 24 бита.
3 Эти типы являются необязательными. Однако если реализация предоставляет целочисленные типы с шириной 8, 16, 32 или 64 бита, без битов заполнения и (для типов со знаком), которые имеют представление дополнения до двух, она должна определить соответствующие имена typedef.
Ничто здесь не упоминает особый подход к поведению операций сдвига битов в этих типах.
Одним из важных аспектов здесь является целочисленное продвижение . Для типов фиксированной ширины, которые меньше int
, они сначала будут переведены в int
(не int16_t
или int32_t
), прежде чем будут применены к большинству операндов. Тогда вы имеете дело с потенциально неопределенным поведением.
Например, предполагая 32-битное int
, этот код демонстрирует неопределенное поведение:
uint24_t x = 0xffffff;
uint24_t y = x << 8;
Поскольку в выражении x << 8
значение x
повышается до int
, то сдвиг вызывает сдвиг бита в знаковый бит этого значения.