Преобразование байтового массива (массив char) в целочисленный тип (short, int, long)
Мне было интересно, имеет ли смысл системность при преобразовании байтового массива в короткий /int/long. Неправильно ли это делать, если код работает как на машинах с большим и низким порядком?
short s = (b[0] << 8) | (b[1]);
int i = (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | (b[3])
Ответы
Ответ 1
Да, констатизм имеет значение. В little endian у вас есть самый старший байт в верхней части коротких или int-i.e бит 8-15 для краткости и 24-31 для int. Для большого эндианта порядок байтов должен быть отменен:
short s = ((b[1] << 8) | b[0]);
int i = (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | (b[0]);
Обратите внимание, что это предполагает, что массив байтов находится в маленьком концевом порядке. Конкретность и преобразование между байтовым массивом и целыми типами зависят не только от конечности ЦП, но и от достоверности данных массива байтов.
Рекомендуется конвертировать эти преобразования в функции, которые будут знать (либо с помощью флагов компиляции, либо во время выполнения) достоверность системы и правильное преобразование.
Кроме того, создание стандарта для данных массива байтов (всегда, например, большого endian, например), а затем с использованием socket
ntoh_s
и ntoh_l
приведет к выгрузке решения относительно сущности в реализацию OS socket
, которая знает о таких вещах. Обратите внимание, что порядок по умолчанию по умолчанию является большим endian (n
in ntoh_x
), поэтому наличие данных байтового массива как большого endian будет самым прямым способом сделать это.
Как указано OP (@Mike), boost
также предоставляет функции преобразования энтитантности.
Ответ 2
// on little endian:
unsigned char c[] = { 1, 0 }; // "one" in little endian order { LSB, MSB }
int a = (c[1] << 8) | c[0]; // a = 1
//--------------------------------------------- -------------------------------
// on big endian:
unsigned char c[] = { 0, 1 }; // "one" in big endian order { MSB, LSB }
int a = (c[1] << 8) | c[0]; // a = 1
//--------------------------------------------- -------------------------------
// on little endian:
unsigned char c[] = { 0, 1 }; // "one" in big endian order { MSB, LSB }
int a = (c[0] << 8) | c[1]; // a = 1 (reverse byte order)
//--------------------------------------------- -------------------------------
// on big endian:
unsigned char c[] = { 1, 0 }; // "one" in little endian order { LSB, MSB }
int a = (c[0] << 8) | c[1]; // a = 1 (reverse byte order)
Ответ 3
Нет, это прекрасно, насколько это касается endianness, но у вас могут быть проблемы, если ваш int
имеет ширину всего 16 бит.
Ответ 4
Проблема, которую вы указали, когда вы используете существующий массив байтов, будет отлично работать на всех машинах. Вы получите тот же ответ.
Однако, в зависимости от того, как вы создаете этот поток, на него может повлиять endianness, и вы не можете рассчитывать на число, которое, по вашему мнению, будет.
Ответ 5
Вы можете использовать для этого объединения. Endianness имеет значение, для его изменения вы можете использовать инструкцию x86 BSWAP (или аналоги для других платформ), предоставляемую большинством компиляторов c как внутреннюю.
#include <stdio.h>
typedef union{
unsigned char bytes[8];
unsigned short int words[4];
unsigned int dwords[2];
unsigned long long int qword;
} test;
int main(){
printf("%d %d %d %d %d\n", sizeof(char), sizeof(short), sizeof(int), sizeof(long), sizeof(long long));
test t;
t.qword=0x0001020304050607u;
printf("%02hhX|%02hhX|%02hhX|%02hhX|%02hhX|%02hhX|%02hhX|%02hhX\n",t.bytes[0],t.bytes[1] ,t.bytes[2],t.bytes[3],t.bytes[4],t.bytes[5],t.bytes[6],t.bytes[7]);
printf("%04hX|%04hX|%04hX|%04hX\n" ,t.words[0] ,t.words[1] ,t.words[2] ,t.words[3]);
printf("%08lX|%08lX\n" ,t.dwords[0] ,t.dwords[1]);
printf("%016qX\n" ,t.qword);
return 0;
}