Преобразование байтового массива (массив 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;
}