在unsigned char数组中设置unsigned short

时间:2018-06-04 08:58:14

标签: c++

我有以下typedef

typedef unsigned char   BYTE;  

typedef unsigned short  WORD;

现在,我有一个看起来像这样的数组

BYTE redundantMessage[6];

和一个看起来像这样的字段

WORD vehicleSpeedToWord = static_cast<WORD>(redundantVelocity);

我想将此消息的第三个和第四个字节设置为值 vehicleSpeedToWord。这会这样做:

redundantMessage[3] = vehicleSpeedToWord;

redundantMessage的第三个字节是否会被自动覆盖?

3 个答案:

答案 0 :(得分:0)

  

我想设置此消息的第三个和第四个字节[fn。 redundantMessage]到vehicleSpeedToWord的值。

Little endian or big endian? 假设unsigned short正好是16位(!)(即sizeof(unsigned short) == 2 && CHAR_BIT == 8,那么:

// little endian
// set the third byte of redundantMessage to  (vehicleSpeedToWord&0xff)
redundantMessage[2] = vehicleSpeedToWord;
// sets the fourth byte of redundantMessage to ((vehicleSpeedToWord&0xff00)>>8)
redundantMessage[3] = vehicleSpeedToWord>>8;

// big endian
redundantMessage[2] = vehicleSpeedToWord>>8;
redundantMessage[3] = vehicleSpeedToWord;

如果要使用主机endianess,则需要告诉编译器分配WORD数据:

*reinterpret_cast<WORD*>(&redundantMessage[2]) = vehicleSpeedToWord;

但这不太可靠 short不是16位,而是至少 16位。因此它可能是x64机器上的64位,或10​​24位机器上的1024位。最好使用fixed width integer types

#include <cstdint>
typedef uint8_t BYTE;
typedef uint16_t WORD;

答案 1 :(得分:0)

正如您所建议的,最好的方法是使用std::memcpy()。但是,您需要传递地址,而不是值;如果你真的是指第三个和第四个字节,它应该从2开始,而不是3

std::memcpy(&redundantDataMessage[2], vehicleSpeedToWord, sizeof(vehicleSpeedToWord));

当然,你可以这样做&#34;手动&#34;通过摆弄比特,例如(假设CHAR_BIT == 8):

const BYTE high = vehicleSpeedToWord >> 8;
const BYTE low = vehicleSpeedToWord & static_cast<WORD>(0x00FF);
redundantDataMessage[2] = high;
redundantDataMessage[3] = low;

不要关心std::memcpy()的性能,生成的代码应该是相同的。

您在评论中讨论的另一点是字节序。如果您正在处理网络协议,则必须实现它们在其中指定的任何字节顺序;并相应地转换。为此,最好的方法是事先将WORD使用某些函数转换为正确的字节序(即从arch的endianness到协议的endianness - 如果转换可能是身份函数,则他们匹配)。

编译器/环境通常定义一组函数来处理它。如果您需要可移植代码,将它们包装在您自己的函数中或实现自己的代码,请参阅How do I convert between big-endian and little-endian values in C++?以获取更多详细信息。

答案 2 :(得分:-1)

您没有说您是希望数据以小端格式(例如英特尔处理器)还是大端(网络字节顺序)存储。

以下是我将如何解决这个问题。

我提供了两个版本进行比较。

#include <cstdint>
#include <type_traits>
#include <cstddef>
#include <iterator>

struct little_endian {}; // low bytes first
struct big_endian {}; // high bytes first

template<class T>
auto integral_to_bytes(T value, unsigned char* target, little_endian)
-> std::enable_if_t<std::is_unsigned_v<T>>
{
    for(auto count = sizeof(T) ; count-- ; )
    {
        *target++ = static_cast<unsigned char>(value & T(0xff));
        value /= 0x100;
    }

}

template<class T>
auto integral_to_bytes(T value, unsigned char* target, big_endian)
-> std::enable_if_t<std::is_unsigned_v<T>>
{
    auto count = sizeof(T);
    auto first = std::make_reverse_iterator(target + count);

    while(count--)
    {
        *first++ = static_cast<unsigned char>(value & T(0xff));
        value /= 0x100;
    }

}


int main()
{
    extern std::uint16_t get_some_value();
    extern void foo(unsigned char*);

    unsigned char buffer[6];
    std::uint16_t some_value = get_some_value();

    // little_endian
    integral_to_bytes(some_value, buffer + 3, little_endian());
    foo(buffer);

    // big-endian
    integral_to_bytes(some_value, buffer + 3, big_endian());    
    foo(buffer);

}

您可以查看生成的汇编程序here。您可以看到,无论哪种方式,编译器都能很好地将逻辑意图转换为非常高效的代码。

更新:我们可以在没有成本的情况下改进样式。现代的c ++编译器很棒:

#include <cstdint>
#include <type_traits>
#include <cstddef>
#include <iterator>

struct little_endian {}; // low bytes first
struct big_endian {}; // high bytes first

template<class T, class Iter> 
void copy_bytes_le(T value, Iter first)
{
    for(auto count = sizeof(T) ; count-- ; )
    {
        *first++ = static_cast<unsigned char>(value & T(0xff));
        value /= 0x100;
    }
}

template<class T, class Iter>
auto integral_to_bytes(T value, Iter target, little_endian)
-> std::enable_if_t<std::is_unsigned_v<T>>
{
    copy_bytes_le(value, target);
}

template<class T, class Iter>
auto integral_to_bytes(T value, Iter target, big_endian)
-> std::enable_if_t<std::is_unsigned_v<T>>
{
    copy_bytes_le(value, 
                  std::make_reverse_iterator(target + sizeof(T)));
}


int main()
{
    extern std::uint16_t get_some_value();
    extern void foo(unsigned char*);

    unsigned char buffer[6];
    std::uint16_t some_value = get_some_value();

    // little_endian
    integral_to_bytes(some_value, buffer + 3, little_endian());
    foo(buffer);

    // big-endian
    integral_to_bytes(some_value, buffer + 3, big_endian());    
    foo(buffer);
}