温馨提示:本文翻译自stackoverflow.com,查看原文请点击:endianness - is C++ abstraction Endian neutral?
c++ endianness modbus modbus-tcp

endianness - C ++抽象字节序是中性的吗?

发布于 2020-12-14 03:20:42

假设我有一个客户机和一个服务器,它们通过某些网络协议(例如ModbusTCP)相互通信16位数字,但是该协议在这里不相关。

现在我知道,客户端的字节序很小(我的电脑),而服务器的字节序很大。(在某些PLC上),客户端完全是使用Boost Asio套接字的C ++编写的。通过这种设置,我认为我必须交换从服务器接收的字节以将数字正确存储在uint16_t变量中,但是这是错误的,因为我正在读取不正确的值。

到目前为止,我的理解是,我的C ++抽象将值正确存储到变量中,而无需我真正关心交换或字节序。请考虑以下代码段:

// received 0x0201  (513 in big endian)
uint8_t high { 0x02 };  // first byte
uint8_t low { 0x01 };   // second byte
// merge into 16 bit value (no swap)
uint16_t val = (static_cast<uint16_t>(high)<< 8) | (static_cast<uint16_t>(low));
std::cout<<val;   //correctly prints 513

这让我有些惊讶,也因为如果我用指针查看内存表示,我发现它们实际上存储在客户端的小字节序中:

// take the address of val, convert it to uint8_t pointer
auto addr = static_cast<uint8_t*>(&val);
// take the first and second bytes and print them 
printf ("%d ", (int)addr[0]);   // print 1
printf ("%d", (int)addr[1]);    // print 2

所以问题是:

只要我不弄乱内存地址和指针,C ++就可以保证我从网络读取的值是正确的,无论服务器的字节序是正确的吗?还是我在这里想念什么?

编辑: 感谢您的回答,我想补充一下,我当前正在使用它boost::asio::write(socket, boost::asio::buffer(data))来将数据从客户端发送到服务器,并且数据是std::vector<uint8_t>因此,我的理解是,只要我按网络顺序填充数据,就不必担心系统(甚至服务器的16位数据)的字节顺序,因为我是在“值”上操作,而不是直接读取字节从记忆中吧?

要使用htons一系列功能,我必须更改要使用的基本TCP层memcpy或类似的以及一个uint8_t*数据缓冲区,即更多的C风格而不是C ++ ish,为什么我要这样做?我没有看到优势吗?

查看更多

提问者
Federico Spinelli
被浏览
0
Alan Birtles 2020-06-23 00:43

(static_cast<uint16_t>(high)<< 8) | (static_cast<uint16_t>(low)) 无论字节序如何,字节都具有相同的行为,数字的“左”端将始终是最高有效位,字节序仅会改变该位在第一个还是最后一个字节中。

例如:

uint16_t input = 0x0201;
uint8_t leftByte = input >> 8; // same result regardless of endianness
uint8_t rightByte = input & 0xFF; // same result regardless of endianness
uint8_t data[2];
memcpy(data, &input, sizeof(input)); // data will be {0x02, 0x01} or {0x01, 0x02} depending on endianness

另一个方向也是如此:

uint8_t data[] = {0x02, 0x01};
uint16_t output1;
memcpy(&output1, data, sizeof(output1)); // will be 0x0102 or 0x0201 depending on endianness
uint16_t output2 = data[1] << 8 | data[0]; // will be 0x0201 regardless of endianness

为确保您的代码在所有平台上都能正常使用,请最好使用htonsntohs系列功能:

uint16_t input = 0x0201; // input is in host order
uint16_t networkInput = htons(input);
uint8_t data[2];
memcpy(data, &networkInput , sizeof(networkInput));
// data is big endian or "network" order
uint16_t networkOutput;
memcpy(&networkOutput, &data, sizeof(networkOutput));
uint16_t output = ntohs(networkOutput);  // output is in host order