Подтвердить что ты не робот

С++: как сделать 2 байта в массиве беззнаковым коротким

Я работаю над унаследованным С++-приложением и, безусловно, вне зоны комфорта (хорошо). Мне было интересно, если кто-нибудь там будет так добр, чтобы дать мне несколько указателей (каламбур).

Мне нужно отбросить 2 байта в неподписанном массиве char к unsigned short. Байты являются последовательными.

Пример того, что я пытаюсь сделать:

Я получаю строку из сокета и помещаю его в массив без знака char. Я могу игнорировать первый байт, а затем следующие 2 байта должны быть преобразованы в unsigned char. Это будет только на окнах, поэтому нет проблем с большими/маленькими Endian (что я знаю).

Вот что я сейчас (не работает явно):

//packetBuffer is an unsigned char array containing the string "123456789" for testing
//I need to convert bytes 2 and 3 into the short, 2 being the most significant byte
//so I would expect to get 515 (2*256 + 3) instead all the code I have tried gives me
//either errors or 2 (only converting one byte
unsigned short myShort;
myShort = static_cast<unsigned_short>(packetBuffer[1])
4b9b3361

Ответ 1

Ну, вы увеличиваете char на короткое значение. Вы хотите интерпретировать два байта как короткие. static_cast не может отбрасываться от unsigned char* до unsigned short*. Вы должны наложить на void*, затем на unsigned short*:

unsigned short *p = static_cast<unsigned short*>(static_cast<void*>(&packetBuffer[1]));

Теперь вы можете разыменовать p и получить короткое значение. Но проблема с этим подходом заключается в том, что вы отбрасываете из unsigned char *, в void *, а затем в другой тип. Стандарт не гарантирует, что адрес остается прежним (и, кроме того, разыменование этого указателя будет undefined). Лучший подход - использовать бит-сдвиг, который всегда будет работать:

unsigned short p = (packetBuffer[1] << 8) | packetBuffer[2];

Ответ 2

Это, вероятно, намного ниже того, о чем вы заботитесь, но имейте в виду, что вы можете легко получить неприсоединившийся доступ, делая это. x86 прощает, а прерывание, которое приведет к негласному доступу, будет зажато внутри и закончится копированием и возвратом значения, поэтому ваше приложение не будет знать ничего другого (хотя оно значительно медленнее, чем выравниваемый доступ). Если, однако, этот код будет работать на не-x86 (вы не укажете целевую платформу, поэтому я предполагаю, что для настольных компьютеров x86 для Windows), то это приведет к сбою данных процессора, и вам придется вручную копировать данные к выровненному адресу, прежде чем пытаться его использовать.

Короче говоря, если вы собираетесь делать этот доступ много, вы можете посмотреть на внесение корректировок в код, чтобы не иметь невысоких чтений, и вы увидите преимущество в использовании.

Ответ 3

Сдвиг бит выше имеет ошибку:

unsigned short p = (packetBuffer[1] << 8) | packetBuffer[2];

если packetBuffer находится в байтах (8 бит в ширину), то вышеуказанный сдвиг может и превратит packetBuffer в ноль, оставив вас только с packetBuffer[2];

Несмотря на это, предпочтение отдается указателям. Чтобы избежать описанной выше проблемы, я трачу несколько строк кода (кроме оптимизации с полным литеральным нулем), в результате получается тот же машинный код:

unsigned short p;
p = packetBuffer[1]; p <<= 8; p |= packetBuffer[2];

Или сохранить некоторые такты и не сдвигать бит с конца:

unsigned short p;
p = (((unsigned short)packetBuffer[1])<<8) | packetBuffer[2];

Вы должны быть осторожны с указателями, оптимизатор укусит вас, а также выравнивает память и длинный список других проблем. Да, сделано правильно, это быстрее, сделано неправильно, ошибка может задерживаться в течение длительного времени и ударяться, когда наименее желательно.

Скажите, что вы ленивы и хотели выполнить 16-битную математику в 8-битном массиве. (маленький конец)

unsigned short *s;
unsigned char b[10];

s=(unsigned short *)&b[0];

if(b[0]&7)
{
   *s = *s+8;
   *s &= ~7;
}

do_something_With(b);

*s=*s+8;

do_something_With(b);

*s=*s+8;

do_something_With(b);

Нет никакой гарантии, что совершенно бесплатный компилятор создаст ожидаемый код. Байт-массив b, отправленный в функцию do_something_with(), никогда не может быть изменен операциями *s. Ничто в коде выше не говорит, что должно. Если вы не оптимизируете свой код, вы никогда не увидите эту проблему (пока кто-то не оптимизирует или не изменяет компиляторы или версии компилятора). Если вы используете отладчик, вы никогда не увидите эту проблему (пока не стало слишком поздно).

Компилятор не видит связи между s и b, это два полностью отдельных элемента. Оптимизатор может выбрать не записывать *s обратно в память, потому что он видит, что *s имеет несколько операций, поэтому он может сохранять это значение в регистре и сохранять его в памяти в конце (если когда-либо).

Существует три основных способа устранения проблемы с указателем выше:

  • Объявить s как volatile.
  • Используйте объединение.
  • Используйте функцию или функции при изменении типов.

Ответ 4

Не следует указывать неподписанный указатель char в коротком указателе без знака (на этот раз от указателя меньшего типа данных до более крупного типа данных). Это связано с тем, что предполагается, что адрес будет правильно выровнен. Лучше всего перевести байты в настоящий неподписанный короткий объект или memcpy в беззнаковый короткий массив.

Без сомнения, вы можете настроить параметры компилятора, чтобы обойти это ограничение, но это очень тонкая вещь, которая будет ломаться в будущем, если код будет передан и повторно использован.

Ответ 5

unsigned short myShort = *(unsigned short *)&packetBuffer[1];

Ответ 6

Возможно, это очень позднее решение, но я просто хочу поделиться с вами. Когда вы хотите конвертировать примитивы или другие типы, вы можете использовать union. См. Ниже:

union CharToStruct {
    char charArray[2];
    unsigned short value;
};


short toShort(char* value){
    CharToStruct cs;
    cs.charArray[0] = value[1]; // most significant bit of short is not first bit of char array
    cs.charArray[1] = value[0];
    return cs.value;
}

Когда вы создаете массив с шестнадцатеричными значениями и вызываете функциюShort, вы получите короткое значение с 3.

char array[2]; 
array[0] = 0x00;
array[1] = 0x03;
short i = toShort(array);
cout << i << endl; // or printf("%h", i);

Ответ 7

static cast имеет другой синтаксис, плюс вам нужно работать с указателями, что вы хотите сделать:

unsigned short *myShort = static_cast<unsigned short*>(&packetBuffer[1]);

Ответ 8

Никто не видел, чтобы вход был строкой!

/* If it is a string as explicitly stated in the question.
 */
int byte1 = packetBuffer[1] - '0'; // convert 1st byte from char to number.
int byte2 = packetBuffer[2] - '0';

unsigned short result = (byte1 * 256) + byte2;

/* Alternatively if is an array of bytes.
 */
int byte1 = packetBuffer[1];
int byte2 = packetBuffer[2];

unsigned short result = (byte1 * 256) + byte2;

Это также позволяет избежать проблем с выравниванием, которые могут быть реализованы большинством других решений на определенных платформах. Примечание. Короткое значение не менее двух байтов. Большинство систем выдаст вам ошибку памяти, если вы попытаетесь удалить ссылку на короткий указатель, который не выровнен по 2 байтам (или независимо от размера (короткого) в вашей системе)!

Ответ 9

char packetBuffer[] = {1, 2, 3};
unsigned short myShort = * reinterpret_cast<unsigned short*>(&packetBuffer[1]);

Мне (приходилось) делать это все время. большой эндиан - очевидная проблема. Что действительно вас доставит, это неправильные данные, когда машина не любит смешение чтения! (и напишите).

вы можете написать тестовый бросок и утвердить, чтобы убедиться, что он правильно читает. Поэтому, когда вы бегали по машине большого конца или, что еще важнее, машина, которая не любит смешение, читает ошибку утверждения, а не странно сложно отслеживать "ошибку";)

Ответ 10

В окнах вы можете использовать:

unsigned short i = MAKEWORD(lowbyte,hibyte);

Ответ 11

Я понимаю, что это старый поток, и я не могу сказать, что я пробовал все предлагаемые здесь предложения. Я просто настраиваю себя на mfc, и я искал способ конвертировать uint в два байта и обратно на другом конце сокета.

Есть много примеров смещения бит, которые вы можете найти в сети, но никто из них, похоже, не работал. Многие примеры кажутся слишком сложными; Я имею в виду, что мы просто говорим о захвате 2 байтов из uint, отправке их по проводу и повторном подключении их обратно на другой конец, верно?

Это решение, которое я, наконец, придумал:

class ByteConverter
{
public:
 static void uIntToBytes(unsigned int theUint, char* bytes)
  {
   unsigned int tInt = theUint;

   void *uintConverter = &tInt;
   char *theBytes = (char*)uintConverter;

   bytes[0] = theBytes[0];
   bytes[1] = theBytes[1];
  }
 static unsigned int bytesToUint(char *bytes)
  {
   unsigned theUint = 0;

   void *uintConverter = &theUint;
   char *thebytes = (char*)uintConverter;

   thebytes[0] = bytes[0];
   thebytes[1] = bytes[1];

   return theUint;
  }
};

Используется следующим образом:

unsigned int theUint;
char bytes[2];
CString msg;
ByteConverter::uIntToBytes(65000,bytes); theUint = ByteConverter::bytesToUint(bytes);
msg.Format(_T("theUint = %d"), theUint); AfxMessageBox(msg, MB_ICONINFORMATION | MB_OK);

Надеюсь, это поможет кому-то.