Так в C99, было решено ввести несколько определений типов, чтобы разработчики могли легко выражать свои мысли, — целочисленные типы фиксированной ширины. Конечно, необязательно, поскольку мы никогда не хотим навредить переносимости. Среди них uint8_t, перенесенный в C++11 как std::uint8_t, 8-битный целочисленный тип без знака фиксированной ширины, был идеальным выбором для людей, которые действительно хотели работать с 8-битными байтами. .
Итак, разработчики воспользовались новыми инструментами и начали создавать библиотеки, в которых четко указано, что они принимают 8-битные последовательности байтов, как std::uint8_t* , std::vector или иначе.
Но, возможно, очень глубоко подумав, комитет по стандартизации решил не требовать реализации std::char_traits, что запрещает разработчикам легко и портативно создавать экземпляры, скажем, std::basic_fstream и легко читать std::uint8_ts как двоичные данные. А может быть, некоторых из нас не волнует количество бит в байте, и их это устраивает.
Но, к сожалению, два мира сталкиваются, и иногда приходится возьмите данные как char* и передайте их в библиотеку, которая ожидает std::uint8_t*. Но подождите, скажете вы, разве переменная char не бита, а std::uint8_t не имеет фиксированного значения 8? Приведет ли это к потере данных?
Ну, по этому поводу есть интересный стандартизм. Тип char, определенный для хранения ровно одного байта, является наименьшим адресуемым фрагментом памяти, поэтому не может быть типа с разрядностью меньше, чем у char. Далее определяется возможность хранения кодовых единиц UTF-8. Это дает нам минимум — 8 бит. Итак, теперь у нас есть определение типа, ширина которого должна составлять 8 бит, и тип шириной не менее 8 бит. Но есть ли альтернативы? Да, беззнаковый символ. Помните, что подпись char определяется реализацией. Любой другой тип? К счастью, нет. Все остальные целочисленные типы имеют обязательные диапазоны, выходящие за пределы 8 бит.
Наконец, std::uint8_t является необязательным, это означает, что библиотека, которая использует это type не будет компилироваться, если он не определен. Но что, если он скомпилируется? Могу с большой долей уверенности сказать, что это означает, что мы находимся на платформе с 8-битными байтами и CHAR_BIT == 8.
Как только мы получим зная, что у нас есть 8-битные байты, что std::uint8_t реализован как char или беззнаковый char, можем ли мы предположить, что мы можем выполнить reinterpret_cast из char* в std::uint8_t* и наоборот? Он портативный?
И здесь меня подводят мои навыки чтения на стандартном языке. Я читал о безопасно производных указателях (
Код: Выделить всё
[basic.stc.dynamic.safety]Код: Выделить всё
std::uint8_t* buffer = /* ... */ ;
char* buffer2 = reinterpret_cast(buffer);
std::uint8_t buffer3 = reinterpret_cast(buffer2);
Итак, при следующих предварительных условиях:
- < ли>
Код: Выделить всё
CHAR_BIT == 8 - определен.
Код: Выделить всё
std::uint8_t
Я бы оцените ссылки на Стандарт с пояснениями.
РЕДАКТИРОВАТЬ: Спасибо, Джерри Коффин. Добавлю цитату из Стандарта ([basic.lval], §3.10/10):
Если программа пытается для доступа к сохраненному значению объекта через glvalue, отличный от одного из
следующих типов, поведение не определено:
...
— тип символа или беззнакового символа.
EDIT2: Хорошо, идем глубже. std::uint8_t не обязательно является определением типа беззнакового символа. Его можно реализовать как расширенный целочисленный тип без знака, а расширенные целочисленные типы без знака не включены в §3.10/10. Что теперь?
Подробнее здесь: https://stackoverflow.com/questions/162 ... nt8-t-safe