【问题标题】:How can i insert integer in char array in c++ using shifting?如何使用移位在 C++ 中的 char 数组中插入整数?
【发布时间】:2023-03-30 18:41:01
【问题描述】:

我真的是 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

【问题讨论】:

  • 到目前为止你有什么想法吗?
  • 如何将整数值转换为数组?
  • 我发现了类似的东西,但这会将其转换为十六进制,我不太了解unsigned char bytes[4]; int n = 175; bytes[0] = (n >> 24) & 0xFF; bytes[1] = (n >> 16) & 0xFF; bytes[2] = (n >> 8) & 0xFF; bytes[3] = n & 0xFF;
  • 您评论中的代码不会将其转换为十六进制。您应该了解& 运算符的作用以及0xFF 代表和用于什么(提示:0xFF 将一个字节的所有 8 位设置为 1)
  • @melk "a char(无符号或无符号)始终为 8 位' - 实际上没有。标准将 char 仅定义为 1 byte (sizeof(char) == 1),但一个字节并非总是定义为 8 位。它在大多数现代系统上都有,但从技术上讲,它是实现定义和平台相关的。@ 987654332@ 定义 CHAR_BIT 以指定一个字节中实际有多少位。它通常是 8,但不是总是。有些系统具有字节大小除了 8 位。

标签: c++ arrays hex ascii


【解决方案1】:

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

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

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

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

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

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

  1. 确定要写入值的第一个字节,由p 指向
  2. 以字节为单位确定写入值的大小,pend 指针在写入值之后定义一个字节。
  3. 通过使用 AND 操作 (&) 和由全 1 组成的掩码“剪切”原始值的第一个字节,并将其分配给 p 指向的位置。
  4. 通过右移 (>>) 从值中删除写入的字节。
  5. 递增p
  6. 如果 (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

在通信中,“网络顺序”被接受为标准,也称为“大端”,其中最重要的字节在前,这需要对上述算法稍作改动。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-21
    • 1970-01-01
    相关资源
    最近更新 更多