Reinterpret_cast между char* и std::uint8_t* – безопасно?C++

Программы на C++. Форум разработчиков
Anonymous
Reinterpret_cast между char* и std::uint8_t* – безопасно?

Сообщение Anonymous »

Теперь нам всем иногда приходится работать с двоичными данными. В C++ мы работаем с последовательностями байтов, и с самого начала нашим строительным блоком был char. Установлено, что sizeof равен 1, это байт. И все библиотечные функции ввода-вывода по умолчанию используют char. Все бы хорошо, но всегда было небольшое беспокойство, небольшая странность, которая беспокоила некоторых людей — количество бит в байте определяется реализацией.

Так в 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);
безопасно, если мы не трогаем буфер2. Поправьте меня, если я ошибаюсь.

Итак, при следующих предварительных условиях:
Переносимо и безопасно ли приводить char* и std::uint8_t*< /code> туда и обратно, предполагая, что мы работаем с двоичными данными и возможное отсутствие знака char не имеет значения?

Я бы оцените ссылки на Стандарт с пояснениями.

РЕДАКТИРОВАТЬ: Спасибо, Джерри Коффин. Добавлю цитату из Стандарта ([basic.lval], §3.10/10):


Если программа пытается для доступа к сохраненному значению объекта через glvalue, отличный от одного из
следующих типов, поведение не определено:

...

— тип символа или беззнакового символа.


EDIT2: Хорошо, идем глубже. std::uint8_t не обязательно является определением типа беззнакового символа. Его можно реализовать как расширенный целочисленный тип без знака, а расширенные целочисленные типы без знака не включены в §3.10/10. Что теперь?

Подробнее здесь: https://stackoverflow.com/questions/162 ... nt8-t-safe

Вернуться в «C++»