如何使用移位在 C++ 中的 char 数组中插入整数?

How can i insert integer in char array in c++ using shifting?

我真的是 c++ 的新手,如果这个问题不好或难以理解,我很抱歉。

我有一个整数,例如,int a;它的值可以在 1 到 3500 之间(这个值是我从文件中得到的)。我还有一个字符数组,unsigned char packet[MAX_PACKET_SIZE];。我的目标是将这个整数值放在这个数组中索引 packet[10] 和 packet[17] 之间所以最后需要 8 个字节。

如果 a 的值为 1,我希望我的数据包数组为:

packet[10] = 30, packet[11] = 30, packet[12] = 30, packet[13] = 30, packet[14] = 30, packet[15] = 30, packet[16] = 30, packet[17] = 31

您必须研究二进制表示和二进制数学才能真正理解按位运算对值的作用。

请注意,3500 很容易适合 16 位值,3500 小于 2^16。 如果你想使用保证大小的类型,你必须使用 uint8_tuint16_t 和类似的。

如果您想要可移植的代码,那么在 C++ 的情况下,此类操作需要谨慎处理。一个 int 可能具有不同的大小甚至不同的字节顺序(字节顺序),但是按位移位运算符 >> 和 << 与字节顺序无关。运算符 << 总是向高位移动,>> 总是向低位移动。

请注意,如果需要,在 C++ 中,所有按位运算的结果都会提升为 unsigned int 或更大的类型。未定义带符号值的移位运算。

在所需算法的朴素但安全的变体中,我们必须执行这些步骤。

(决定我们将字节写入缓冲区的顺序。假设我们从不太重要到比较重要。)

  1. 确定要写入值的第一个字节,由p
  2. 指向
  3. 确定写入值的大小(以字节为单位),pend 指针定义写入值后的一个字节。
  4. 通过与操作(&)和由全1组成的掩码从原始值中“切”出第一个字节,并将其分配给p指向的位置。
  5. 通过右移 (>>) 从值中删除写入的字节。
  6. 增量p.
  7. 如果(p != pend)转到3.

(可选)我们可以保存 ppend 用于进一步的目的,例如用于顺序写入。

在 C 风格(但已经是 C++)变体中,这看起来像:

unsigned char * pack_int(unsigned char *p, unsigned value)
{
    unsigned char *pend = p + sizeof(value);
    while(p != pend)
    {
        // ~ is a bit-wise not, ~0 produces an int with all bits set
        *p = value & ((unsigned char)~0); 
        value >>= CHAR_BIT;
        p++;
    }
    return p;
}

使用 ((unsigned char)~0) 而不是 0xFF 文字只是为了防止不是 8 位的字节。编译器会将其转换为正确的文字值。

C++ 允许使此实现与类型无关。例如。仍然需要顺序迭代器来解决输出位置的一个:

template <class InIt, class T>
InIt pack(InIt p, T value)
{
    using target_t = std::make_unsigned_t<std::remove_reference_t<decltype(*p)>>;
    using src_t = std::make_unsigned_t<T>;
    
    InIt pend = p + sizeof(T);
    src_t val = static_cast<src_t> (value); // if T is signed, it would fit anyway.
    
    while(p != pend)
    {
         *p = (val & (target_t)~0);
        val >>= CHAR_BIT;
        p++;
    }
    
    return pend;
}

在 C++17 中可以使用

InIt pend = p; 
std::advance(pend, sizeof(T));

C++ 中更好的实现是在编译期间通过应用递归模板静态生成转换序列,而不是使用循环。

这是一个功能齐全的程序,同时使用了:

#include <iostream>
#include <array>
#include <climits>
#include <type_traits>

// C-styled function
// packs value into buffer, returns pointer to the byte after its end.
unsigned char * pack_int(unsigned char *p, unsigned value)
{
    unsigned char *pend = p + sizeof(value);
    while(p != pend)
    {
        // ~ is a bit-wise not, ~0 produces an int with all bits set
        *p = value & ((unsigned char)~0); 
        value >>= CHAR_BIT;
        p++;
    }
    return p;
}

// a type-agnostic template
template <class InIt, class T>
InIt pack(InIt p, T value)
{
    using target_t = std::make_unsigned_t<std::remove_reference_t<decltype(*p)>>;
    using src_t = std::make_unsigned_t<T>;
    
    InIt pend = p + sizeof(T);
    src_t val = static_cast<src_t> (value); // if T is signed, it would fit anyway.
    
    while(p != pend)
    {
         *p = (val & (target_t)~0);
        val >>= CHAR_BIT;
        p++;
    }
    
    return pend;
}


int main(int argc, char** argv )
{
    std::array<unsigned char, 16> buffer = {};
    
    auto  ptr = pack_int(&(buffer[0]), 0xA4B3C2D1);
    
    ptr = pack(ptr, (long long)0xA4B3C2D1);   
    pack(ptr, 0xA4B3C2D1);   

    std::cout << std::hex;
    for( auto c : buffer)
        std::cout << +c << ", ";

    std::cout << "{end}\n";
}

这个输出将是

d1, c2, b3, a4, d1, c2, b3, a4, 0, 0, 0, 0, d1, c2, b3, a4, {end}

序列d1, c2, b3, a4,重复两次,显然是十六进制值0xA4B3C2D1的反向表示。在匹配内存中 unsigned int 表示的小端系统上。对于 3500(十六进制 0xDAC),它将是 ac, d, 0, 0.

在通信中,“网络顺序”被接受为标准,也称为“big-endian”,其中最高有效字节在前,这需要对上述算法稍作改动。