将char数组反序列化为int和int []以及struct?

时间:2022-09-06 11:01:49

Can I populate a struct by treating it as an array of unsigned char values?

我可以通过将结构视为无符号字符值数组来填充结构吗?

I receive character data over the RS232 UART from a Microcontroller. Using C, how can I deserialize the char Array in my Datatypes uint16_t, uin32_t, int32_t[], ...

我通过RS232 UART从微控制器接收字符数据。使用C,我如何反序列化我的数据类型中的char数组uint16_t,uin32_t,int32_t [],...

e.g.

uin16_t StartID = ARRAY[0];
uin16_t EndID = ARRAY[246];

... is there something like this ? In C# there are effective functions for deserialization but im not that in C

......有这样的东西吗?在C#中有反序列化的有效功能,但不是在C中

for later

if (StartID == 11)
    printf("good"); 
else 
    printf("bad");

I made this struct to work easier later with the Data. Is there a way to get the Data from my ARRAY[248] into my struct?

我以后使用Data使这个结构更容易工作。有没有办法从我的ARRAY [248]获取数据到我的结构?

typedef struct
{
  //Header 6byte
  uint16_t StartID; // Check if its 1 or in hex 0b
  uint16_t ID;      
  uint16_t LoadID;  

  //Payload 240 byte
  int32_t POS01[3];  
  int32_t POS02[3];
  int32_t POS03[3];
  int32_t POS04[3];
  int32_t POS05[3];
  int32_t POS06[3];
  int32_t POS07[3];
  int32_t POS08[3];
  int32_t POS09[3];
  int32_t POS10[3];
  int32_t POS11[3];
  int32_t POS12[3];
  int32_t POS13[3];
  int32_t POS14[3];
  int32_t POS15[3];
  int32_t POS16[3];
  int32_t POS17[3];
  int32_t POS18[3];
  int32_t POS19[3];
  int32_t POS20[3];

  //End 2byte
  uint16_t EndID; // Check if its 47 or in hex 2F

} DATA_POS; //TOTAL = 248byte

2 个解决方案

#1


0  

C has a feature called pointers:

C有一个叫做指针的功能:

uin16_t *StartID = (void*)&ARRAY[0];
uin16_t *EndID = (void*)&ARRAY[246];

Other means to access data under another format include unions:

以其他格式访问数据的其他方法包括工会:

   union 
   {
      uint32_t *u32;
      uint16_t *u16;
      uint8_t  *u8;
   } u;

As you can see, sometimes features do not need a dedicated function.

如您所见,有时功能不需要专用功能。

Beware however that alignment issues could arise on some platforms because of accessing data in such a way.

但请注意,由于以这种方式访问​​数据,在某些平台上可能会出现对齐问题。

#2


0  

There are a number of similar approaches. You could use a C union, which avoids the use of explicit pointers. You could use 'memcpy' to copy the contents of the unsigned char[] into the memory of a DATA_POS structure. Or you could write directly over that memory while reading from the serial port.

有许多类似的方法。您可以使用C联合,这可以避免使用显式指针。您可以使用'memcpy'将unsigned char []的内容复制到DATA_POS结构的内存中。或者,您可以在从串行端口读取时直接在该存储器上写入。

You didn't mention whether endianness is a concern, so I'll address that too.

你没有提到endianness是否是一个问题,所以我也会解决这个问题。

A union lets you access the same block of memory as different data types. For example:

通过联合,您可以访问与不同数据类型相同的内存块。例如:

#include <stdio.h>

#define chars_per_int (sizeof(int) / sizeof(char))

// Note that every member of a union exists at the same memory location,
// so changing the value of one will change the value of all
union IntAsChars {
    unsigned int integerValue;
    unsigned char characterValue[chars_per_int];
};

int main() {
    int i;
    union IntAsChars value;

    value.integerValue = 0x1234abcd;

    for (i = 0; i < chars_per_int; i++) {
        printf("Character value is: %x\n", value.characterValue[i]);
    }

    return 0;
}

The above code treats the same sizeof(int) bytes as a string of character values, and prints the numeric value of each.

上面的代码将相同的sizeof(int)字节视为一串字符值,并打印每个字符的数值。

Based on your code, you're looking for a union like

根据你的代码,你正在寻找像这样的联盟

union data_representation {
    unsigned char char_data[248];
    DATA_POS data_pos;
};

union data_representation data;

This would let you fill data.char_data from the serial part, but refer to data.data_pos.StartID from your code.

这将允许您从串行部分填充data.char_data,但请参阅代码中的data.data_pos.StartID。

Note, however, that different architectures can store the individual bytes of an integer in different order. Intel architectures traditionally use small-endian representations. The output of the code above will be different on x86:

但请注意,不同的体系结构可以按不同的顺序存储整数的各个字节。英特尔架构传统上使用小端表示。上面代码的输出在x86上会有所不同:

Character value is: cd
Character value is: ab
Character value is: 34
Character value is: 12

and SPARC:

Character value is: 12
Character value is: 34
Character value is: ab
Character value is: cd

The safest general approach is to send big-endian data between systems (in this case, via the serial port) and use ntohs / ntohl to convert to the local host's representation. These functions convert "network" (big-endian) short (ntohs) or long (ntohl) data to whatever format is used by the local system. ntoh means network to host, and the suffix is for short or long values. There are corresponding htons and htonl functions for converting from the host format into big-endian.

最安全的通用方法是在系统之间发送大端数据(在这种情况下,通过串行端口)并使用ntohs / ntohl转换为本地主机的表示。这些函数将“网络”(大端)短(ntohs)或长(ntohl)数据转换为本地系统使用的任何格式。 ntoh表示网络托管,后缀表示短值或长值。有相应的htons和htonl函数用于从主机格式转换为big-endian。

So you'd still populate data.char_data, but read the data using ntohs(data.data_pos.StartID) and ntohl(data.data_pos.POS04[1]) to get the native values.

所以你仍然填充data.char_data,但是使用ntohs(data.data_pos.StartID)和ntohl(data.data_pos.POS04 [1])读取数据以获取本机值。

It might even make sense to make your own conversion routines so you don't have ntohl/ntohs sprinkled throughout your code later. For example, theconvert_to_host_format` function below converts a 248-character input array to a DATA_POS structure with the appropriate endianness.

制作自己的转换例程甚至可能是有意义的,因此您以后的代码中不会出现ntohl / ntoh。例如,下面的contvert_to_host_format`函数将248个字符的输入数组转换为具有适当字节顺序的DATA_POS结构。

void convert_POS_to_host_format (int32_t dest[3], int32_t src[3]) {
    dest[0] = ntohl(src[0]);
    dest[1] = ntohl(src[1]);
    dest[2] = ntohl(src[2]);
}

DATA_POS convert_to_host_format (char buffer[248]) {
    DATA_POS host_data;
    union data_representation data;

    memcpy((void*)data.char_data, (void*)buffer, sizeof(buffer));

    host_data.StartID = ntohs(data.data_pos.StartID);
    host_data.ID = ntohs(data.data_pos.ID);
    host_data.LoadID = ntohs(data.data_pos.LoadID);

    convert_POS_to_host_format (host_data.POS01, data.data_pos.POS01);
    convert_POS_to_host_format (host_data.POS02, data.data_pos.POS02);
    convert_POS_to_host_format (host_data.POS03, data.data_pos.POS03);

    /* ... */

    convert_POS_to_host_format (host_data.POS19, data.data_pos.POS19);
    convert_POS_to_host_format (host_data.POS20, data.data_pos.POS20);

    host_data.EndID = ntohs(data.data_pos.EndID);

    return host_data;
}

If you wanted to skip the union, memcpy could be used to achieve the same results by writing directly over the memory holding the contents of DATA_POS:

如果你想跳过联合,可以通过直接写入包含DATA_POS内容的内存来使用memcpy来获得相同的结果:

void convert_POS_to_host_format (int32_t POS[3]) {
    POS[0] = ntohl(POS[0]);
    POS[1] = ntohl(POS[1]);
    POS[2] = ntohl(POS[2]);
}

DATA_POS convert_to_host_format (char buffer[sizeof(DATA_POS)]) {
    DATA_POS host_data;

    /* Write the contents of 'buffer' directly into the memory location of
     * 'host_data'.
    memcpy((void*)host_data, (void*)buffer, sizeof(DATA_POS));

    /* Convert values to host byte order in place. */
    host_data.StartID = ntohs(host_data.StartID);
    host_data.ID = ntohs(host_data.ID);
    host_data.LoadID = ntohs(host_data.LoadID);

    convert_POS_to_host_format (host_data.POS01);
    convert_POS_to_host_format (host_data.POS02);
    convert_POS_to_host_format (host_data.POS03);

    /* ... */

    convert_POS_to_host_format (host_data.POS19);
    convert_POS_to_host_format (host_data.POS20);

    host_data.EndID = ntohs(data.data_pos.EndID);

    return host_data;
}

You could even use a pointer while reading the UART data to write directly into the DATA_POS structure. The below treats the address of the structure (&data) as an unsigned character pointer.

您甚至可以在读取UART数据时使用指针直接写入DATA_POS结构。下面将结构(&data)的地址视为无符号字符指针。

It would still be a good idea to use a function like the convert_to_host_format structure above to address endianness concerns. If you don't need to be able to run on multiple architectures, though, the following should be enough to meet your needs.

使用类似上面的convert_to_host_format结构的函数来解决字节顺序问题仍然是个好主意。但是,如果您不需要能够在多个体系结构上运行,则以下内容应足以满足您的需求。

DATA_POS data;
int i;
char *write_buffer;

write_buffer = (unsigned char*) &data;

for (i = 0; i < sizeof(DATA_POS); i++) {
    write_buffer[i] = read_byte_from_uart();
}

Please let me know if there's anything you'd like me to clarify.

如果您有什么需要我澄清的话,请告诉我。

#1


0  

C has a feature called pointers:

C有一个叫做指针的功能:

uin16_t *StartID = (void*)&ARRAY[0];
uin16_t *EndID = (void*)&ARRAY[246];

Other means to access data under another format include unions:

以其他格式访问数据的其他方法包括工会:

   union 
   {
      uint32_t *u32;
      uint16_t *u16;
      uint8_t  *u8;
   } u;

As you can see, sometimes features do not need a dedicated function.

如您所见,有时功能不需要专用功能。

Beware however that alignment issues could arise on some platforms because of accessing data in such a way.

但请注意,由于以这种方式访问​​数据,在某些平台上可能会出现对齐问题。

#2


0  

There are a number of similar approaches. You could use a C union, which avoids the use of explicit pointers. You could use 'memcpy' to copy the contents of the unsigned char[] into the memory of a DATA_POS structure. Or you could write directly over that memory while reading from the serial port.

有许多类似的方法。您可以使用C联合,这可以避免使用显式指针。您可以使用'memcpy'将unsigned char []的内容复制到DATA_POS结构的内存中。或者,您可以在从串行端口读取时直接在该存储器上写入。

You didn't mention whether endianness is a concern, so I'll address that too.

你没有提到endianness是否是一个问题,所以我也会解决这个问题。

A union lets you access the same block of memory as different data types. For example:

通过联合,您可以访问与不同数据类型相同的内存块。例如:

#include <stdio.h>

#define chars_per_int (sizeof(int) / sizeof(char))

// Note that every member of a union exists at the same memory location,
// so changing the value of one will change the value of all
union IntAsChars {
    unsigned int integerValue;
    unsigned char characterValue[chars_per_int];
};

int main() {
    int i;
    union IntAsChars value;

    value.integerValue = 0x1234abcd;

    for (i = 0; i < chars_per_int; i++) {
        printf("Character value is: %x\n", value.characterValue[i]);
    }

    return 0;
}

The above code treats the same sizeof(int) bytes as a string of character values, and prints the numeric value of each.

上面的代码将相同的sizeof(int)字节视为一串字符值,并打印每个字符的数值。

Based on your code, you're looking for a union like

根据你的代码,你正在寻找像这样的联盟

union data_representation {
    unsigned char char_data[248];
    DATA_POS data_pos;
};

union data_representation data;

This would let you fill data.char_data from the serial part, but refer to data.data_pos.StartID from your code.

这将允许您从串行部分填充data.char_data,但请参阅代码中的data.data_pos.StartID。

Note, however, that different architectures can store the individual bytes of an integer in different order. Intel architectures traditionally use small-endian representations. The output of the code above will be different on x86:

但请注意,不同的体系结构可以按不同的顺序存储整数的各个字节。英特尔架构传统上使用小端表示。上面代码的输出在x86上会有所不同:

Character value is: cd
Character value is: ab
Character value is: 34
Character value is: 12

and SPARC:

Character value is: 12
Character value is: 34
Character value is: ab
Character value is: cd

The safest general approach is to send big-endian data between systems (in this case, via the serial port) and use ntohs / ntohl to convert to the local host's representation. These functions convert "network" (big-endian) short (ntohs) or long (ntohl) data to whatever format is used by the local system. ntoh means network to host, and the suffix is for short or long values. There are corresponding htons and htonl functions for converting from the host format into big-endian.

最安全的通用方法是在系统之间发送大端数据(在这种情况下,通过串行端口)并使用ntohs / ntohl转换为本地主机的表示。这些函数将“网络”(大端)短(ntohs)或长(ntohl)数据转换为本地系统使用的任何格式。 ntoh表示网络托管,后缀表示短值或长值。有相应的htons和htonl函数用于从主机格式转换为big-endian。

So you'd still populate data.char_data, but read the data using ntohs(data.data_pos.StartID) and ntohl(data.data_pos.POS04[1]) to get the native values.

所以你仍然填充data.char_data,但是使用ntohs(data.data_pos.StartID)和ntohl(data.data_pos.POS04 [1])读取数据以获取本机值。

It might even make sense to make your own conversion routines so you don't have ntohl/ntohs sprinkled throughout your code later. For example, theconvert_to_host_format` function below converts a 248-character input array to a DATA_POS structure with the appropriate endianness.

制作自己的转换例程甚至可能是有意义的,因此您以后的代码中不会出现ntohl / ntoh。例如,下面的contvert_to_host_format`函数将248个字符的输入数组转换为具有适当字节顺序的DATA_POS结构。

void convert_POS_to_host_format (int32_t dest[3], int32_t src[3]) {
    dest[0] = ntohl(src[0]);
    dest[1] = ntohl(src[1]);
    dest[2] = ntohl(src[2]);
}

DATA_POS convert_to_host_format (char buffer[248]) {
    DATA_POS host_data;
    union data_representation data;

    memcpy((void*)data.char_data, (void*)buffer, sizeof(buffer));

    host_data.StartID = ntohs(data.data_pos.StartID);
    host_data.ID = ntohs(data.data_pos.ID);
    host_data.LoadID = ntohs(data.data_pos.LoadID);

    convert_POS_to_host_format (host_data.POS01, data.data_pos.POS01);
    convert_POS_to_host_format (host_data.POS02, data.data_pos.POS02);
    convert_POS_to_host_format (host_data.POS03, data.data_pos.POS03);

    /* ... */

    convert_POS_to_host_format (host_data.POS19, data.data_pos.POS19);
    convert_POS_to_host_format (host_data.POS20, data.data_pos.POS20);

    host_data.EndID = ntohs(data.data_pos.EndID);

    return host_data;
}

If you wanted to skip the union, memcpy could be used to achieve the same results by writing directly over the memory holding the contents of DATA_POS:

如果你想跳过联合,可以通过直接写入包含DATA_POS内容的内存来使用memcpy来获得相同的结果:

void convert_POS_to_host_format (int32_t POS[3]) {
    POS[0] = ntohl(POS[0]);
    POS[1] = ntohl(POS[1]);
    POS[2] = ntohl(POS[2]);
}

DATA_POS convert_to_host_format (char buffer[sizeof(DATA_POS)]) {
    DATA_POS host_data;

    /* Write the contents of 'buffer' directly into the memory location of
     * 'host_data'.
    memcpy((void*)host_data, (void*)buffer, sizeof(DATA_POS));

    /* Convert values to host byte order in place. */
    host_data.StartID = ntohs(host_data.StartID);
    host_data.ID = ntohs(host_data.ID);
    host_data.LoadID = ntohs(host_data.LoadID);

    convert_POS_to_host_format (host_data.POS01);
    convert_POS_to_host_format (host_data.POS02);
    convert_POS_to_host_format (host_data.POS03);

    /* ... */

    convert_POS_to_host_format (host_data.POS19);
    convert_POS_to_host_format (host_data.POS20);

    host_data.EndID = ntohs(data.data_pos.EndID);

    return host_data;
}

You could even use a pointer while reading the UART data to write directly into the DATA_POS structure. The below treats the address of the structure (&data) as an unsigned character pointer.

您甚至可以在读取UART数据时使用指针直接写入DATA_POS结构。下面将结构(&data)的地址视为无符号字符指针。

It would still be a good idea to use a function like the convert_to_host_format structure above to address endianness concerns. If you don't need to be able to run on multiple architectures, though, the following should be enough to meet your needs.

使用类似上面的convert_to_host_format结构的函数来解决字节顺序问题仍然是个好主意。但是,如果您不需要能够在多个体系结构上运行,则以下内容应足以满足您的需求。

DATA_POS data;
int i;
char *write_buffer;

write_buffer = (unsigned char*) &data;

for (i = 0; i < sizeof(DATA_POS); i++) {
    write_buffer[i] = read_byte_from_uart();
}

Please let me know if there's anything you'd like me to clarify.

如果您有什么需要我澄清的话,请告诉我。