У меня есть поток байтов, например
uint8_t buf[] = { 0xFF, 0x1B, … , 0x34 }; // approx. 800 Bytes
Теперь я хочу извлечь различные целые числа из известных позиций и сохранить их в своих собственных переменных, например «получить int64 из индекса 0x3B буфера» или «получить uint32 из индекса 0x03».
Как лучше всего это сделать?
Поток имеет обратный порядок байтов, но я мог бы использовать замену байтов (bswap64()
) из заголовков ESP32.
Существует несколько способов преобразования нескольких байтов в целое число фиксированного размера, один из них:
#include <stdlib.h>
#include <stdint.h>
static size_t get_integer(uint8_t *buf, size_t start, size_t count)
{
size_t i, result = 0;
for (i = 0; i < count; i++)
result |= (size_t)buf[i] << ((count - i - 1) * CHAR_BIT);
return result;
}
int main()
{
uint8_t buf[] = { 0x00, 0x00, 0x01, 0x00, 0x32, 0x34, 0x23, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00 };
int r1 = (int)get_integer(buf, 0, sizeof(int));
long r2 = (long long)get_integer(buf, 8, sizeof(long long));
}
используя функцию для преобразования. Другой менее безопасен и заключается в приведении указателя и помещении его значения непосредственно в целочисленную переменную:
int r1 = *(int *)(buf + 0); // where 0 and 12 are the beginning positions
long r2 = *(long *)(buf + 12);
Спасибо, я пытался реализовать ваше второе предложение, но не получил бит «+ 12». Это работает как шарм.
@dexamenos Пожалуйста, «+ 12» обозначает позицию первого байта числа в буфере (индекс), таким образом увеличивая указатель, чтобы он указывал на ячейку памяти, с которой начинается число. Затем вы преобразуете указатель в нужный вам размер шрифта, получаете доступ к его значению и получаете искомое число.
Нет никаких причин упоминать «другой» метод, кроме как научить студентов тому, что всякий раз, когда они видят его в коде, им следует его исправить. Это нарушает правила псевдонимов в C 2018 6.5 7, и в этом нет необходимости, поскольку существуют другие методы переинтерпретации байтов, определенные стандартом C, в частности, простое копирование байтов в желаемый тип объекта, как в случае с memcpy
. Далее, хорошие компиляторы распознают такой memcpy
и оптимизируют его.
Если у вас есть данные, хранящиеся в виде массива uint8_t (с прямым порядком байтов), например:
uint8_t data[12]= { 0xe8, 0x03, 0x00, 0x00, 0xe9, 0x03, 0x00, 0x00, 0xea, 0x03, 0x00, 0x00}; // 3 uint32_t value (in little endian) of 1000, 1001, and 1002
Все, что вам нужно, это указать правильное местоположение и получить данные как uint32_t с указателем на это местоположение и соблюдать его.
uint8_t data[12]= { 0xe8, 0x03, 0x00, 0x00, 0xe9, 0x03, 0, 0, 0xea, 0x03, 0, 0};
int idx = 1; // get the second uint32_t value which is 1001 (0x000003e9)
uint32_t data1 = *(uint32_t*) &data[idx * 4];
Помимо того недостатка, что она работает только в том случае, если поток имеет правильный порядок байтов, эта версия может вызывать неопределенное поведение, если буфер не просто случайно выровнен должным образом для 32-битного доступа.
Помимо проблемы с выравниванием, это нарушает требования к псевдонимам в C 2018 6.5 7. Правильный способ переосмысления байтов из такого буфера — скопировать их в объект uint32_t
, как в случае с uint32_t data1; memcpy(&data1, data + offset, sizeof data1);
.