我有一個結構體變數:
typedef struct message_t
{
uint16_t time;
uint16_t lat;
uint8_t ns;
uint16_t lon;
uint8_t ew;
} message;
message msg;
msg.time = 0x1234;
msg.lat = 0x2122;
msg.ns = 'n';
msg.lon = 0x2234;
msg.ew = 'e';
uint8_t msg_arr[8];
如何轉換msg為 uint8_t 陣列msg_arr[8]?
通過使用 memcpy 來轉換資料,
memcpy(msg_arr, &msg, sizeof(msg_arr));
for(int i=0; i < 8; i ){
printf("msg[%d]: %d \n", i, msg_arr[i]);
}
我得到輸出:
msg[0]: 52
msg[1]: 18
msg[2]: 34
msg[3]: 33
msg[4]: 110
msg[5]: 0
msg[6]: 52
msg[7]: 34
使用 memcpy 似乎有問題。例如,msg[5], msg[6] and msg[7]不正確。
uj5u.com熱心網友回復:
答案 1/3:使用聯合和打包結構
另請參閱我在這里更長的答案:使用聯合進行轉換的可移植性
您可以使用聯合轉換為位元組陣列。請務必打包結構以洗掉填充位元組。
typedef struct __attribute__ ((__packed__)) message_s
{
uint16_t time;
uint16_t lat;
uint8_t ns;
uint16_t lon;
uint8_t ew;
} message_t;
typedef union message_converter_u
{
message_t message;
uint8_t bytes[sizeof(message_t)];
} message_converter_t;
現在通過message_converter_t聯合進行轉換:
message_t message =
{
.time = 0x1234,
.lat = 0x2122,
.ns = 'n', // 0x6E
.lon = 0x1834,
.ew = 'e', // 0x65
};
message_converter_t converter;
converter.message = message;
就是這樣!
converter.bytes現在神奇地是一個uint8_t包含 8 個元素的陣列,其中包含結構的所有位元組。
然而,它有位元組序的考慮!
下面是一些列印所有位元組的示例列印代碼:
// Print the bytes
printf("bytes = [");
for (size_t i = 0; i < sizeof(converter.bytes); i )
{
printf("0xX", converter.bytes[i]);
if (i < sizeof(converter.bytes) - 1)
{
printf(", ");
}
}
printf("]\n");
以及 64 位小端x86 架構 Linux 機器上的輸出:
bytes = [0x34, 0x12, 0x22, 0x21, 0x6E, 0x34, 0x18, 0x65]
Notice that due to my machine being little-endian, the least-significant-byte 0x34 comes first in the time variable of 0x1234. So, you get 0x34 and then 0x12. This happens with all of the multi-byte variables. To remove endianness considerations across hardware architectures, you'd have to move to a bit-shifting approach instead of using a union--see my link above for examples and more details, and also see my Answer 2/3 here.
Full, runnable example
struct_to_array_via_type_punning_union.c: <-- download it as part of my eRCaGuy_hello_world repo
#include <stdbool.h> // For `true` (`1`) and `false` (`0`) macros in C
#include <stdint.h> // For `uint8_t`, `int8_t`, etc.
#include <stdio.h> // For `printf()`
typedef struct message_unpacked_s
{
uint16_t time;
uint16_t lat;
uint8_t ns;
uint16_t lon;
uint8_t ew;
} message_unpacked_t;
typedef struct __attribute__ ((__packed__)) message_s
{
uint16_t time;
uint16_t lat;
uint8_t ns;
uint16_t lon;
uint8_t ew;
} message_t;
typedef union message_converter_u
{
message_t message;
uint8_t bytes[sizeof(message_t)];
} message_converter_t;
// int main(int argc, char *argv[]) // alternative prototype
int main()
{
printf("This is the start of `main()`.\n");
// demonstrate that packing the struct matters
printf("sizeof(message_unpacked_t) = %zu bytes\n", sizeof(message_unpacked_t)); // 10 bytes due to padding
printf("sizeof(message_t) = %zu bytes\n", sizeof(message_t)); // 8 bytes
message_t message =
{
.time = 0x1234,
.lat = 0x2122,
.ns = 'n', // 0x6E
.lon = 0x1834,
.ew = 'e', // 0x65
};
message_converter_t converter;
// Note: copying `message` into `converter.message` here is unnecessarily inefficient. A
// more-efficient way is to simply construct the union type alone and populate the struct
// data inside the union directly. See "struct_to_array_via_type_punning_union_more_efficient.c"
// for that demo.
converter.message = message;
// Print the bytes
printf("bytes = [");
for (size_t i = 0; i < sizeof(converter.bytes); i )
{
printf("0xX", converter.bytes[i]);
if (i < sizeof(converter.bytes) - 1)
{
printf(", ");
}
}
printf("]\n");
return 0;
}
Build and run command:
mkdir -p bin && gcc -Wall -Wextra -Werror -O3 -std=c11 -save-temps=obj struct_to_array_via_type_punning_union.c \
-o bin/struct_to_array_via_type_punning_union && bin/struct_to_array_via_type_punning_union
Sample output:
eRCaGuy_hello_world/c$ mkdir -p bin && gcc -Wall -Wextra -Werror -O3 -std=c11 -save-temps=obj struct_to_array_via_type_punning_union.c \ > -o bin/struct_to_array_via_type_punning_union && bin/struct_to_array_via_type_punning_union This is the start of `main()`. sizeof(message_unpacked_t) = 10 bytes sizeof(message_t) = 8 bytes bytes = [0x34, 0x12, 0x22, 0x21, 0x6E, 0x34, 0x18, 0x65]
More-efficient technique: do NOT copy from a struct to a union! Just use the union alone as your message_t!
struct_to_array_via_type_punning_union_more_efficient.c
#include <stdbool.h> // For `true` (`1`) and `false` (`0`) macros in C
#include <stdint.h> // For `uint8_t`, `int8_t`, etc.
#include <stdio.h> // For `printf()`
typedef struct message_data_unpacked_s
{
uint16_t time;
uint16_t lat;
uint8_t ns;
uint16_t lon;
uint8_t ew;
} message_data_unpacked_t;
typedef struct __attribute__ ((__packed__)) message_data_s
{
uint16_t time;
uint16_t lat;
uint8_t ns;
uint16_t lon;
uint8_t ew;
} message_data_t;
typedef union message_u
{
message_data_t data;
uint8_t bytes[sizeof(message_data_t)];
} message_t;
// int main(int argc, char *argv[]) // alternative prototype
int main()
{
printf("This is the start of `main()`.\n");
// demonstrate that packing the struct matters
printf("sizeof(message_data_unpacked_t) = %zu bytes\n", sizeof(message_data_unpacked_t)); // 10 bytes due to padding
printf("sizeof(message_data_t) = %zu bytes\n", sizeof(message_data_t)); // 8 bytes
message_t message =
{
.data =
{
.time = 0x1234,
.lat = 0x2122,
.ns = 'n', // 0x6E
.lon = 0x1834,
.ew = 'e', // 0x65
},
};
// Print the bytes
printf("bytes = [");
for (size_t i = 0; i < sizeof(message.bytes); i )
{
printf("0xX", message.bytes[i]);
if (i < sizeof(message.bytes) - 1)
{
printf(", ");
}
}
printf("]\n");
return 0;
}
Sample output is the same as before:
eRCaGuy_hello_world/c$ mkdir -p bin && gcc -Wall -Wextra -Werror -O3 -std=c11 -save-temps=obj struct_to_array_via_type_punning_union_more_efficient.c \ > -o bin/struct_to_array_via_type_punning_union_more_efficient && bin/struct_to_array_via_type_punning_union_more_efficient This is the start of `main()`. sizeof(message_data_unpacked_t) = 10 bytes sizeof(message_data_t) = 8 bytes bytes = [0x34, 0x12, 0x22, 0x21, 0x6E, 0x34, 0x18, 0x65]
Keywords: c type punning; c struct to byte array conversion
uj5u.com熱心網友回復:
答案 2/3:通過手動位移將結構體轉換為位元組陣列
通過位移位將結構轉換為 C 中的陣列,這與聯合技術不同,沒有基于架構的位元組順序問題或差異,這些問題可能因硬體架構而異!相反,手動位移消除了位元組順序問題,代價是手動將位元組從結構復制到陣列中(而聯合和原始 ptr 技術不需要復制任何位元組)!
如果您需要在不同位元組序的不同系統之間發送序列化資料,這種與位元組序無關的技術使其比其他兩種方法(聯合和原始 ptr)更好。
struct_to_array_via_bit_shifting.c:
#include <stdbool.h> // For `true` (`1`) and `false` (`0`) macros in C
#include <stdint.h> // For `uint8_t`, `int8_t`, etc.
#include <stdio.h> // For `printf()`
// Read a single byte at index `byte_num` from any multi-byte variable.
#define BYTE(value, byte_num) ((uint8_t)(((value) >> (8*(byte_num))) & 0xff))
// Notice with this technique the struct does NOT need to be packed! This is contrary to the
// union technique.
typedef struct message_s
{
uint16_t time;
uint16_t lat;
uint8_t ns;
uint16_t lon;
uint8_t ew;
} message_t;
void message_struct_to_array(const message_t* message, uint8_t* bytes)
{
bytes[0] = BYTE(message->time, 0);
bytes[1] = BYTE(message->time, 1);
bytes[2] = BYTE(message->lat, 0);
bytes[3] = BYTE(message->lat, 1);
bytes[4] = BYTE(message->ns, 0);
bytes[5] = BYTE(message->lon, 0);
bytes[6] = BYTE(message->lon, 1);
bytes[7] = BYTE(message->ew, 0);
}
// int main(int argc, char *argv[]) // alternative prototype
int main()
{
printf("Answer 2/3: convert a struct to an array of bytes via manual bit-shifting.\n");
message_t message =
{
.time = 0x1234,
.lat = 0x2122,
.ns = 'n', // 0x6E
.lon = 0x1834,
.ew = 'e', // 0x65
};
// NB: this is NOT the same thing as `sizeof(message_t)` due to padding bytes which are in the
// `message_t` struct!
const size_t MESSAGE_NUM_BYTES = sizeof(message.time) sizeof(message.lat)
sizeof(message.ns) sizeof(message.lon) sizeof(message.ew);
uint8_t bytes[MESSAGE_NUM_BYTES];
message_struct_to_array(&message, bytes);
// Print the bytes
printf("bytes = [");
for (size_t i = 0; i < sizeof(bytes); i )
{
printf("0xX", bytes[i]);
if (i < sizeof(bytes) - 1)
{
printf(", ");
}
}
printf("]\n");
return 0;
}
示例輸出:
eRCaGuy_hello_world/c$ mkdir -p bin && gcc -Wall -Wextra -Werror -O3 -std=c11 -save-temps=obj struct_to_array_via_bit_shifting.c \ > -o bin/struct_to_array_via_bit_shifting && bin/struct_to_array_via_bit_shifting Answer 2/3: convert a struct to an array of bytes via manual bit-shifting. bytes = [0x34, 0x12, 0x22, 0x21, 0x6E, 0x34, 0x18, 0x65]
uj5u.com熱心網友回復:
答案 3/3:使用打包結構和uint8_t指向它的原始指標
通過原始指標將結構轉換為 C 中的陣列,而無需對資料進行任何復制,例如通過 memcpy 或通過位移位方法轉換為陣列。這也有架構位元組序問題,就像使用聯合一樣。
不過這個概念超級簡單。只需打包結構,然后將其視為一個位元組陣列! - 像這樣:
// Use a raw pointer
uint8_t * bytes = (uint8_t*)(&message);
它本質上是一個明確的、手動的聯合,因為您只是“通過另一個鏡頭”查看資料,我喜歡這樣稱呼它,而不是實際復制、操作或更改資料。
完整示例:
struct_to_array_via_type_punning_raw_pointer.c:
#include <stdbool.h> // For `true` (`1`) and `false` (`0`) macros in C
#include <stdint.h> // For `uint8_t`, `int8_t`, etc.
#include <stdio.h> // For `printf()`
typedef struct message_unpacked_s
{
uint16_t time;
uint16_t lat;
uint8_t ns;
uint16_t lon;
uint8_t ew;
} message_unpacked_t;
// This MUST be packed to work properly without having padding bytes in the byte array!
typedef struct __attribute__ ((__packed__)) message_s
{
uint16_t time;
uint16_t lat;
uint8_t ns;
uint16_t lon;
uint8_t ew;
} message_t;
// int main(int argc, char *argv[]) // alternative prototype
int main()
{
printf("Answer 3/3: use a packed struct and a raw `uint8_t` pointer to it`.\n");
// demonstrate that packing the struct matters
printf("sizeof(message_unpacked_t) = %zu bytes\n", sizeof(message_unpacked_t)); // 10 bytes due to padding
printf("sizeof(message_t) = %zu bytes\n", sizeof(message_t)); // 8 bytes
message_t message =
{
.time = 0x1234,
.lat = 0x2122,
.ns = 'n', // 0x6E
.lon = 0x1834,
.ew = 'e', // 0x65
};
// Use a raw pointer
uint8_t * bytes = (uint8_t*)(&message);
// Print the bytes of `message`
printf("bytes = [");
for (size_t i = 0; i < sizeof(message); i )
{
printf("0xX", bytes[i]);
if (i < sizeof(message) - 1)
{
printf(", ");
}
}
printf("]\n");
return 0;
}
構建并運行命令:
mkdir -p bin && gcc -Wall -Wextra -Werror -O3 -std=c11 -save-temps=obj struct_to_array_via_type_punning_raw_pointer.c \
-o bin/struct_to_array_via_type_punning_raw_pointer && bin/struct_to_array_via_type_punning_raw_pointer
示例輸出:
eRCaGuy_hello_world/c$ mkdir -p bin && gcc -Wall -Wextra -Werror -O3 -std=c11 -save-temps=obj struct_to_array_via_type_punning_raw_pointer.c -o bin/struct_to_array_via_type_punning_raw_pointer && bin/struct_to_array_via_type_punning_raw_pointer Answer 3/3: use a packed struct and a raw `uint8_t` pointer to it`. sizeof(message_unpacked_t) = 10 bytes sizeof(message_t) = 8 bytes bytes = [0x34, 0x12, 0x22, 0x21, 0x6E, 0x34, 0x18, 0x65]
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/357603.html
標籤:C
上一篇:沒有動態分配的鏈表
