Многие методы принимают char за «байтовый тип» 1. Однако неясно (для меня), является ли поведение, определяемое реализацией, то, что должно делать приведение от unsigned char к char, когда значение unsigned char больше, чем CHAR_MAX.
Этот раздел взят из C++11 (ISO/IEC 14882:2011) §4.7 Интегральные преобразования [conv.integral/2]:
Если тип назначения подписан, значение не изменяется, если оно может быть представлено в типе назначения (и ширине битового поля); в противном случае значение определяется реализацией.
Этот раздел стандарта также упоминается в аналогичном вопросе, но не упоминается, как мы можем сохранить битовый шаблон для этих преобразований.
Итак, как мне привести uint8_t, unsigned char или другой тип ширины символа к char и обратно, сохраняя битовый шаблон?
Примечание. Этот вопрос касается C++ в целом, а не конкретной версии. Если что-то было неприемлемо в C++11, но работало в C++14, то было бы полезно, если бы ответ мог противопоставить две версии.
1: например, встроенные функции SIMD x64, такие как __m128i _mm_set1_epi8(char), используйте char.
Цитата, о которой вы говорите, предназначена для преобразований, т. е. signed char foo = unsigned_vchar_vat Выполнение conat char * data = &unsigned_variable позволяет вам проверять байты unsigned_variable.
@Jason Если вы можете процитировать, что стандарт гарантирует переносимость, вы можете опубликовать это как ответ.
@NathanOliver Если вы считаете, что это рекомендуемый (и указанный) способ преобразования с сохранением битового шаблона (т. е. *(unsigned char *)&ch или что-то подобное), это ответ.
Я не думаю, что это включено в стандарт, потому что C++ должен работать на машинах, не имеющих дополнительных процессоров. Многие из них вы больше не найдете, и беспокоиться о них — это суперпедантично.
@Джейсон, тебе даже не гарантировано, сколько бит в типе char. Беспокойство по поводу странной архитектуры может показаться педантичным до тех пор, пока вы не будете вынуждены делать это ради своей работы.
@MarkRansom, вам гарантировано, что char и unsigned char имеют одинаковое количество бит. В стандарте это не указано, но даже на машинах с дополнением, отличным от 2, приведения здесь не меняют биты, если только реализация не является намеренно тупой.
@Джейсон Это было изменено в C++20. Все целочисленные типы теперь должны дополняться двумя.
@MarkRansom Если вы обобщаете процессоры, которые не являются 8-битными символами, это абсурдно педантично. Я работаю в мире встроенных систем и видел больше странных архитектур, чем большинство других. Я никогда не видел процессора, в котором не было бы 8-битных символов и дополнения до 2.





В C++20 вы можете использовать std::bit_cast. Это гарантирует, что вы получите побитовое идентичное значение для типов без битов заполнения, которыми являются char и unsigned char.
До этого вам придется изучить документацию каждой реализации, которую вы собираетесь использовать. Все реализации1, о которых я знаю, предпочитают не изменять битовые шаблоны для достижения значений, определенных реализацией.
Формулировка в стандарте существует, потому что битовые комбинации, соответствующие отрицательным числам, представляют разные числа в дополнении до двух, дополнении до единицы, а также знаке и величине.
В противном случае вы можете std::memcpy lvalue unsigned char в lvalue char
-0, и в итоге вы получите значение, отличное от того, которое дала бы машина с дополнением до двух, но если реализация не изо всех сил старается быть тупой, она будет иметь тот же битовый шаблон.Нет ли четкого способа приведения битов unsigned char к char до C++20?
@doliphin всегда есть memcpy
@Калет, это достойно еще одного ответа.
@MarkRansom Я углубился в подробности.
Причина, по которой memcpy работает, заключается в том, что он даже не предполагает копирования типа char, он принимает void* параметры. Это настолько чистая копия памяти, насколько это возможно в C/C++.
std::bit_cast — надгробие memcpy. Все варианты использования memcpy покрываются безопасными и эффективными стандартными функциями, начиная с C++20. memcpy имеет множество предостережений и подводных камней, что делает использование кода подверженным ошибкам. Единственным оставшимся вариантом использования до C++20 была игра слов типов (как в OP). memcpy вызовы должны генерироваться компилятором или реализацией стандартной библиотеки. Пользовательский код C++, содержащий прямые вызовы этой функции, вызывает тревогу. Предпочитайте std::bit_cast, если можете переключиться на C++20 или выше.
Приведение между знаковыми и беззнаковыми типами одного и того же размера не меняет битовую комбинацию благодаря дополнению 2.