动漫人物
您面临的问题与结构成员的对齐方式有关。您可以在此处阅读有关它的更多信息,但简而言之,C++ 编译器有时会添加填充字节以保持架构所期望的自然对齐。如果不使用该对齐方式,可能会导致性能下降甚至访问冲突。例如,对于 x86/x64,大多数类型的对齐方式通常(但不一定保证)与大小相同。我们可以看到#include <cstdint>#include <type_traits>std::size_t offsets[] = { std::alignment_of_v<std::uint8_t>, std::alignment_of_v<std::uint16_t>, std::alignment_of_v<std::uint32_t>, std::alignment_of_v<std::uint64_t>, std::alignment_of_v<__uint128_t>, std::alignment_of_v<std::int8_t>, std::alignment_of_v<std::int16_t>, std::alignment_of_v<std::int32_t>, std::alignment_of_v<std::int64_t>, std::alignment_of_v<__int128_t>, std::alignment_of_v<float>, std::alignment_of_v<double>, std::alignment_of_v<long double>, std::alignment_of_v<void*>,};编译为offsets: .quad 1 .quad 2 .quad 4 .quad 8 .quad 16 .quad 1 .quad 2 .quad 4 .quad 8 .quad 16 .quad 4 .quad 8 .quad 16 .quad 8由于这些(和其他)实现细节,建议不要依赖内部表示。但是,在某些情况下,其他方法可能不够快(例如逐字段序列化),或者您可能无法更改 C++ 代码,例如 OP。binary.Read需要打包数据,但 C++ 将使用填充。我们需要使用依赖于编译器的指令,例如#pragma pack(1)或添加填充 Go 结构。第一个不是 OP 的选项,所以我们将使用第二个。我们可以使用offsetof宏来确定结构成员相对于结构本身的偏移量。我们可以做类似的事情#include <array>#include <cstddef>#include <cstdint>using int8 = std::int8_t;using uint8 = std::uint8_t;using uint16 = std::uint16_t;struct CarTelemetryData { uint16 m_speed; uint8 m_throttle; int8 m_steer; uint8 m_brake; uint8 m_clutch; int8 m_gear; uint16 m_engineRPM; uint8 m_drs; uint8 m_revLightsPercent; uint16 m_brakesTemperature[4]; uint16 m_tyresSurfaceTemperature[4]; uint16 m_tyresInnerTemperature[4]; uint16 m_engineTemperature; float m_tyresPressure[4];};// C++ has no reflection (yet) so we need to list every memberconstexpr auto offsets = std::array{ offsetof(CarTelemetryData, m_speed), offsetof(CarTelemetryData, m_throttle), offsetof(CarTelemetryData, m_steer), offsetof(CarTelemetryData, m_brake), offsetof(CarTelemetryData, m_clutch), offsetof(CarTelemetryData, m_gear), offsetof(CarTelemetryData, m_engineRPM), offsetof(CarTelemetryData, m_drs), offsetof(CarTelemetryData, m_revLightsPercent), offsetof(CarTelemetryData, m_brakesTemperature), offsetof(CarTelemetryData, m_tyresSurfaceTemperature), offsetof(CarTelemetryData, m_tyresInnerTemperature), offsetof(CarTelemetryData, m_engineTemperature), offsetof(CarTelemetryData, m_tyresPressure),};constexpr auto sizes = std::array{ sizeof(CarTelemetryData::m_speed), sizeof(CarTelemetryData::m_throttle), sizeof(CarTelemetryData::m_steer), sizeof(CarTelemetryData::m_brake), sizeof(CarTelemetryData::m_clutch), sizeof(CarTelemetryData::m_gear), sizeof(CarTelemetryData::m_engineRPM), sizeof(CarTelemetryData::m_drs), sizeof(CarTelemetryData::m_revLightsPercent), sizeof(CarTelemetryData::m_brakesTemperature), sizeof(CarTelemetryData::m_tyresSurfaceTemperature), sizeof(CarTelemetryData::m_tyresInnerTemperature), sizeof(CarTelemetryData::m_engineTemperature), sizeof(CarTelemetryData::m_tyresPressure),};constexpr auto computePadding() { std::array<std::size_t, offsets.size()> result; std::size_t expectedOffset = 0; for (std::size_t i = 0; i < offsets.size(); i++) { result.at(i) = offsets.at(i) - expectedOffset; expectedOffset = offsets.at(i) + sizes.at(i); } return result;}auto padding = computePadding();编译为(constexprFTW)padding: .quad 0 .quad 0 .quad 0 .quad 0 .quad 0 .quad 0 .quad 1 .quad 0 .quad 0 .quad 0 .quad 0 .quad 0 .quad 0 .quad 2所以,在 x86 上,我们需要一个字节之前EngineRPM和两个字节之前TyresPressure。所以,让我们检查一下它是否有效。C++:#include <cstddef>#include <cstdint>#include <iomanip>#include <iostream>#include <span>using int8 = std::int8_t;using uint8 = std::uint8_t;using uint16 = std::uint16_t;struct CarTelemetryData { uint16 m_speed; uint8 m_throttle; int8 m_steer; uint8 m_brake; uint8 m_clutch; int8 m_gear; uint16 m_engineRPM; uint8 m_drs; uint8 m_revLightsPercent; uint16 m_brakesTemperature[4]; uint16 m_tyresSurfaceTemperature[4]; uint16 m_tyresInnerTemperature[4]; uint16 m_engineTemperature; float m_tyresPressure[4];};int main() { CarTelemetryData data = { .m_speed = 1, .m_throttle = 2, .m_steer = 3, .m_brake = 4, .m_clutch = 5, .m_gear = 6, .m_engineRPM = 7, .m_drs = 8, .m_revLightsPercent = 9, .m_brakesTemperature = {10, 11, 12, 13}, .m_tyresSurfaceTemperature = {14, 15, 16, 17}, .m_tyresInnerTemperature = {18, 19, 20, 21}, .m_engineTemperature = 22, .m_tyresPressure = {23, 24, 25, 26}, }; std::cout << "b := []byte{" << std::hex << std::setfill('0'); for (auto byte : std::as_bytes(std::span(&data, 1))) { std::cout << "0x" << std::setw(2) << static_cast<unsigned>(byte) << ", "; } std::cout << "}";}结果是b := []byte{0x01, 0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x07, 0x00, 0x08, 0x09, 0x0a, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x0f, 0x00, 0x10, 0x00, 0x11, 0x00, 0x12, 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x41, 0x00, 0x00, 0xc0, 0x41, 0x00, 0x00, 0xc8, 0x41, 0x00, 0x00, 0xd0, 0x41, }让我们在 Go 中使用它:// Type your code here, or load an example.// Your function name should start with a capital letter.package mainimport ( "bytes" "encoding/binary" "fmt")type CarTelemetryData struct { Speed uint16 Throttle uint8 Steer int8 Brake uint8 Clutch uint8 Gear int8 _ uint8 EngineRPM uint16 DRS uint8 RevLightsPercent uint8 BrakesTemperature [4]uint16 TyresSurfaceTemperature [4]uint16 TyresInnerTemperature [4]uint16 EngineTemperature uint16 _ uint16 TyresPressure [4]float32}func main() { b := []byte{0x01, 0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x07, 0x00, 0x08, 0x09, 0x0a, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x0f, 0x00, 0x10, 0x00, 0x11, 0x00, 0x12, 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x41, 0x00, 0x00, 0xc0, 0x41, 0x00, 0x00, 0xc8, 0x41, 0x00, 0x00, 0xd0, 0x41} var dataStruct CarTelemetryData dataReader := bytes.NewReader(b[:]) binary.Read(dataReader, binary.LittleEndian, &dataStruct) fmt.Printf("%+v", dataStruct)}哪个打印{Speed:1 Throttle:2 Steer:3 Brake:4 Clutch:5 Gear:6 _:0 EngineRPM:7 DRS:8 RevLightsPercent:9 BrakesTemperature:[10 11 12 13] TyresSurfaceTemperature:[14 15 16 17] TyresInnerTemperature:[18 19 20 21] EngineTemperature:22 _:0 TyresPressure:[23 24 25 26]}取出填充字节,它会失败。