Проблема при копировании байтового массива в структуру c - PullRequest
2 голосов
/ 20 сентября 2019

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

https://onlinegdb.com/SJtEatMvS

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

Цель

Моя цель в c состоит в том, чтобы скопировать байтовый массив в структуру, сохраняя порядок, в котором был массив (если это имеет смысл).Например,

Набор данных содержит:

{0x12, 0x34, 0x56, 0x78, 0x0A, 0x06, 0x77}

И определение структуры:

typedef struct {
    uint8_t  test0;
    uint16_t test1;
    uint32_t test2;
} Foo_t;

Я бы хотел, чтобы 0x12 было скопировано в test0, {0x3456} скопировано в test1и {0x780A0677} скопированы в test2.Как уже упоминалось выше, я использовал небольшой массив для тестирования, но реальный массив довольно большой, поэтому назначение членов структуры вручную для меня не вариант.

Я знаю, memcpy не проблема, так как не заботится о порядке байтов, и реальная проблема - мои предположения о том, как данные должны быть выровнены.С точки зрения хоста, он работает в системе Windows, которая, как я считаю, имеет мало порядковый номер.

Ответы [ 3 ]

1 голос
/ 20 сентября 2019

Endianness связан с CPU , а не с ОС.Но так как Windows работает только на x86, а x86 имеет порядок с прямым порядком байтов, Windows с прямым порядком байтов (ну, у них, по-видимому, также есть версия ARM, но большинство ARM также имеют порядок с прямым порядком байтов).

Как вашданные имеют порядок байтов, вам нужно преобразовать их в порядок байтов вашего процессора.Но big-endian также является стандартным сетевым порядком байтов, поэтому вы можете полагаться на функции ntoh*(), чтобы сделать это за вас.К сожалению, это означает, что вам придется делать это вручную для каждого поля ...

Если бы ваш ЦП имел большие байты, вы могли бы упаковать свою структуру с #pragma pack(1) и memcpy() it (или привести указатель).

1 голос
/ 20 сентября 2019

Я удаляю свой исходный ответ из-за неполноты понимания вашей проблемы.Теперь я понял это после прочтения следующей статьи: Написание независимого от порядкового номера кода в C

Сначала проблема с выравниванием:

Как упомянуто500 - Внутренняя ошибка сервера

У вас будут проблемы с копированием данных, поскольку ваша структура будет содержать отступы.В вашем примере к структуре будет добавлен 1 байт.

Ниже приведен пример макета памяти, полученного из VS при реализации 32-битного языка C.

size = 8
Address of test0        = 5504200
Padding added here at address 5504201
Address of test1        = 5504202
Address of test2        = 5504204

Чтобы указать правила выравнивания,компилятор должен использовать, используйте директиву препроцессора pack .

// Aligns on byte boundaries, then restore alignment value to system defaults
#pragma pack ( 1 )
#pragma pack ()

// Aligns on byte boundaries, restores previously assigned alignment value.
#pragma pack ( push, 1 )
#pragma pack (pop)

Используя ваш пример, определение структуры будет выглядеть примерно так:

#pragma pack ( 1 )
typedef struct {
    unsigned char  test0;
    unsigned short test1;
    unsigned int   test2;
} Foo_t;
#pragma pack ()

Foo_t s2;

printf("\nsize = %d\n", sizeof(Foo_t));

printf("   Address of test0        = %u\n", &s2.test0);
printf("   Address of test1        = %u\n", &s2.test1);
printf("   Address of test2        = %u\n", &s2.test2);

Результат:

size = 7
Address of test0        = 10287904
Address of test1        = 10287905
Address of test2        = 10287907

Вторая проблема с порядком байтов:

Проблема заключается в том, как целые числа хранятся в памяти на 32-разрядных компьютерах с архитектурой x86.На машинах x86 они хранятся в порядке с прямым порядком байтов.

Например, при копировании 2-байтового массива, содержащего байты x34 и x56, в короткое целое число будет храниться как x56 (младший байт) x34 (следующий байт)).Это не то, что вы хотели.

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

Пример:

int main()
{

#pragma pack ( 1 )
typedef struct {
    unsigned char  test0;
    unsigned short test1;
    unsigned int   test2;
} Foo_t;
#pragma pack ()

    unsigned char tempBuf[7] = { 0x12, 0x34, 0x56, 0x78, 0x0A, 0x06, 0x77 };

    Foo_t foo;

    memcpy(&foo, &tempBuf[0], 7);

    //foo.test0 = netToHost (&foo,0,1);  // not needed
    foo.test1 = reverseByteOrder(&foo, 1, 2);
    foo.test2 = reverseByteOrder(&foo, 3, 4);

    printf("\n After memcpy We have %02X %04X %08X\n", foo.test0, foo.test1, foo.test2);
}


int reverseByteOrder(char array[], int startIndex, int size)
{
    int intNumber =0;

    for (int i = 0; i < size; i++)
        intNumber = (intNumber << 8) | array[startIndex + i];

    return intNumber;
}

Вывод:

After memcpy We have 12 3456 780A0677
0 голосов
/ 20 сентября 2019
uint8_t data[] = {0x12, 0x34, 0x56, 0x78, 0x0A, 0x06, 0x77};
typedef struct {
    uint8_t  test0;
    uint16_t test1;
    uint32_t test2;
} Foo_t;

Foo_t fs;
fs.test0 = data[0];
fs.test1 = data[1]<<8 + data[2];
fs.test2 = data[3]<<24 + data[4]<<16 + data[5]<<8 + data[6];

Если ваш процессор имеет порядковый номер с прямым порядком байтов, вы можете немного обмануть ..

fs.test0 = data[0];
fs.test1 = *(uint16_t*)&data[1];
fs.text2 = *(uint32_t*)&data[3];

Если в вашем массиве есть байты в порядке, соответствующем вашему процессору, а в вашей структуре много переменныхВы можете использовать атрибут __packed и memcpy ().

typedef __packed struct {
    uint8_t  test0;
    uint16_t test1;
    uint32_t test2;
} Foo_t;

Foo_t fs;
memcpy(&fs, data, sizeof(fs));
...