Сужающее преобразование в C++

В Руководстве Beej по сетевому программированию есть функция это должно было обеспечить переносимый способ сериализации 16-битного целого числа.

/*
** packi16() -- store a 16-bit int into a char buffer (like htons())
*/ 
void packi16(unsigned char *buf, unsigned int i)
{
    *buf++ = i>>8; *buf++ = i;
}

Я не понимаю, почему оператор *buf++ = i; является переносимым, поскольку присвоение целого числа без знака (i) беззнаковому символу (*buf) приведет к сужающему преобразованию.

  • Гарантирует ли стандарт С++, что при таком преобразовании unsigned int всегда усекается, а его младшие 8 битов сохраняются в unsigned char?
  • Если нет, есть ли предпочтительный способ решить проблему? Достаточно ли изменить тело функции на следующее?

    *buf++ = (i>>8) & 0xFFFFU; *buf++ = i & 0xFFFFU;


person user740006    schedule 07.09.2014    source источник
comment
Обратите внимание, что unsigned int теперь редко бывает 16-битным. Кроме этого, в назначении будет использоваться число, которое соответствует типу назначения и конгруэнтно по модулю 2ⁿ, где n — количество битов в типе назначения.   -  person chris    schedule 07.09.2014


Ответы (2)


Код предполагает 8-битный байт, а это непереносимо.

Например. некоторые процессоры цифровых сигналов Texas Instruments имеют 16-битный байт.

Количество битов в байте определяется CHAR_BIT из <limits.h>.

Кроме того, код предполагает, что unsigned является 16-битным, что не является переносимым.

Таким образом, код не является переносимым.


Re

Гарантирует ли стандарт C++, что при таком преобразовании unsigned int всегда усекается, а его младшие 8 битов сохраняются в unsigned char?

Нет, так как стандарт C++ не гарантирует, что количество битов на байт равно 8.

Единственная гарантия состоит в том, что это минимум 8 бит.

Однако беззнаковая арифметика гарантированно является модульной.


Re

Если нет, есть ли предпочтительный способ решить проблему?

Используйте простой цикл, повторяющийся sizeof(unsigned) раза.

Рассматриваемый код, по-видимому, был получен из такого цикла, поскольку постинкремент в *buf++ = i; совершенно бессмысленен (это последнее использование buf).

person Cheers and hth. - Alf    schedule 07.09.2014
comment
Если в системе нет 8-битных байтов, то неясно, состоит ли намерение в том, чтобы разделить ввод на октеты или разделить его на байты. ИМХО, в этом случае переносимость следует понимать как переносимость в другие ситуации с 8-битными байтами. - person M.M; 07.09.2014
comment
Спасибо за ответ. Можно вопрос вдогонку? Если CHAR_BIT>8 и я прочитаю/запишу unsigned char из/в двоичный файл, все ли CHAR_BIT биты x и y будут прочитаны/записаны или только 8 бит (и какие 8 бит)? - person user740006; 07.09.2014
comment
@ user740006: Зависит от кода и абстракции файла. В стандартных потоках С++ f.write( p, 1 ) записывает ровно 1 байт того, на что указывает p, когда f находится в двоичном режиме. То есть единицей памяти C++ является байт с sizeof(char) = 1 по определению, независимо от того, сколько битов в байте. Но если p указывает, скажем, на unsigned, то эффект зависит от архитектуры машины. На машине с прямым порядком байтов, такой как ПК с Windows, он запишет младший значащий байт указателя. На машине с обратным порядком байтов будет записан самый старший байт. Чтобы избежать этого, можно преобразовать в текст. - person Cheers and hth. - Alf; 07.09.2014
comment
@MattMcNabb: я думаю, можно сказать, что он переносим между компиляторами с 8-битным байтом и 16-битным unsigned. Тогда переносимость связана с порядком байтов, а именно с тем, что массив байтов гарантированно заполнен представлением значения с обратным порядком байтов. Но это то, для чего предназначена функция htons (winsock, порядок байтов от хоста до порядка байтов в сети). - person Cheers and hth. - Alf; 07.09.2014
comment
@Cheersandhth.-Альф, я не понимаю. Помимо порядка следования байтов, как можно безопасно передать текстовый файл ASCII, если ширина символа на одной платформе неизвестна другой? Компьютер с CHAR_BIT=10 может записать четырехсимвольную строку ABCD как 0001000001 0001000010 0001000011 0001000011, но компьютер с CHAR_BIT=8 может считывать 8 бит за каждый ход и получить пять символов 00010000 01000100 00100001 00001100 01000011, т.е. страница]С. Что мне не хватает? (Я пришел из других областей, кроме CS. Пожалуйста, извините за мои глупые вопросы.) - person user740006; 07.09.2014
comment
@ user740006: Вы просто упускаете из виду, что сетевые протоколы указывают точные размеры единиц данных, используемых при передаче. Каждая сторона преобразуется в/из этого. Это основная причина, по которой в телекоммуникациях говорят об октетах, а не о байтах. - person Cheers and hth. - Alf; 07.09.2014
comment
@Cheersandhth.-Alf Спасибо за ответ. - person user740006; 08.09.2014

Да, присваивание вне допустимого диапазона беззнаковым типам изменяет значение по модулю на единицу больше, чем максимальное значение, представляемое в типе. В данном случае мод UCHAR_MAX+1.

Исправление не требуется. Некоторые люди любят писать *buf++ = i % 0x100; или что-то подобное, чтобы было понятно, что это было преднамеренное сужение.

person M.M    schedule 07.09.2014