【问题标题】:Integer into char array整数到 char 数组
【发布时间】:2011-12-19 22:30:15
【问题描述】:

我需要在位层将整数值转换为字符数组。假设 int 有 4 个字节,我需要将它分成 4 个长度为 1 字节的块作为 char 数组。

例子:

int a = 22445;
// this is in binary 00000000 00000000 1010111 10101101
...
//and the result I expect
char b[4];
b[0] = 0; //first chunk
b[1] = 0; //second chunk
b[2] = 87; //third chunk - in binary 1010111
b[3] = 173; //fourth chunk - 10101101

我需要这种转换非常快,如果可能的话没有任何循环(可能是一些位操作的技巧)。目标是在一秒钟内完成数千次此类转换。

【问题讨论】:

  • 字节数组和整数的联合有什么问题?
  • 不要同时标记 C 和 C++。 重要 您正在编写代码。C++ 包含显着的内存别名等额外限制。
  • 对不起标签,我删除了C,我在C++项目中需要它。

标签: c++


【解决方案1】:

我不确定我是否推荐这个,但你可以 #include <stddef.h><sys/types.h> 并写:

*(u32_t *)b = htonl((u32_t)a);

htonl是为了确保整数在你存储之前在big-endian order中。)

【讨论】:

  • 我认为您缺少左侧的取消引用(但 IIRC 的语法 - 指针强制转换 + 取消引用为左值 - 可能不是标准的;您始终可以声明一个单独的指针变量)。
  • @MatteoItalia:回复:缺少取消引用:哎呀,哈,你是对的,现在已修复,谢谢!回复:指针转换+取消引用为左值:真的吗?一个指针本身不是左值,但我认为它的解引用。 (参见例如stackoverflow.com/questions/7446489/…,有几个人说是这样,但似乎没有人反驳他们。)
  • @ruakh:嗯,你是对的,我记错了那个问题中讨论的问题。 +1,然后! :)
  • 我希望这个解决方案是可移植的,它运作良好。在 Linux 上,您必须使用 <arpa/inet.h> 并输入 uint32_t。但是像long long 这样更大的数据类型呢?
  • @PeterKrejci: here 你可以找到很多解决方案。
【解决方案2】:
int a = 22445;
char *b = (char *)&a;
char b2 = *(b+2); // = 87
char b3 = *(b+3); // = 173

【讨论】:

  • char*为任何类型起别名是完全合法的。
  • @DeadMG 由于字节序依赖性,它不可移植
【解决方案3】:

根据您希望如何表示负数,您可以简单地转换为unsigned,然后使用掩码和移位:

unsigned char b[4];
unsigned ua = a;

b[0] = (ua >> 24) & 0xff;
b[1] = (ua >> 16) & 0xff;
b[2] = (ua >> 8) & 0xff
b[3] = ua & 0xff;

(由于将负数转换为无符号的 C 规则,这将产生负数的二进制补码表示,这几乎肯定是您想要的)。

【讨论】:

    【解决方案4】:

    要访问任何类型的二进制表示,您可以将指针强制转换为字符指针:

    T x;  // anything at all!
    
    // In C++
    unsigned char const * const p = reinterpret_cast<unsigned char const *>(&x);
    
    /* In C */
    unsigned char const * const p = (unsigned char const *)(&x);
    
    // Example usage:
    for (std::size_t i = 0; i != sizeof(T); ++i)
        std::printf("Byte %u is 0x%02X.\n", p[i]);
    

    也就是说,您可以将p 视为指向数组unsigned char[sizeof(T)] 的第一个元素的指针。 (在你的情况下,T = int。)

    我在这里使用了unsigned char,以便在打印二进制值时不会遇到任何符号扩展问题(例如,在我的示例中通过printf)。如果要将数据写入文件,请改用char

    【讨论】:

      【解决方案5】:

      您已经接受了答案,但我仍然会给出我的答案,这可能更适合您(或相同...)。这是我测试的:

      int a[3] = {22445, 13, 1208132};
      
      for (int i = 0; i < 3; i++)
      {
          unsigned char * c = (unsigned char *)&a[i];
          cout << (unsigned int)c[0] << endl;
          cout << (unsigned int)c[1] << endl;
      
          cout << (unsigned int)c[2] << endl;
          cout << (unsigned int)c[3] << endl;
          cout << "---" << endl;
      }
      

      ...它对我有用。现在我知道您请求了一个 char 数组,但这是等效的。对于第一种情况,您还要求 c[0] == 0, c[1] == 0, c[2] == 87, c[3] == 173,这里顺序颠倒了。

      基本上,您使用 SAME 值,只是以不同的方式访问它。

      你可能会问,为什么我没有使用 htonl()?

      好吧,既然性能是一个问题,我认为你最好不要使用它,因为调用一个确保字节按某种顺序排列的函数似乎是在浪费(宝贵的?)周期,当它们可以有的时候在某些系统上已经按照该顺序,并且如果不是这种情况,您可以修改代码以使用不同的顺序。

      因此,您可以在之前检查订单,然后根据测试结果使用不同的循环(更多代码,但提高性能)。

      另外,如果您不知道您的系统是使用 2 字节还是 4 字节 int,您可以先检查一下,然后根据结果再次使用不同的循环。

      重点是:您将拥有更多代码,但您不会在循环内部的关键区域浪费循环。

      如果您仍然有性能问题,您可以展开循环(在循环内重复代码,并减少循环次数),因为这也可以为您节省几个循环。

      请注意,就 C++ 而言,使用 c[0]、c[1] 等等价于 *(c)、*(c+1)。

      【讨论】:

        【解决方案6】:
        typedef union{
          byte intAsBytes[4];
          int int32;
        }U_INTtoBYTE; 
        

        【讨论】:

        • 在 C++ 中这是未定义的行为。
        • 我预计会出现“不可移植 - 字节序”。如果您有一种检测字节顺序的方法,那么当然可以使用它!你可以用你想要的任何方式读取字节。
        • 一点也不。据我们所知,OP 实际上有可能想要调查类型是如何在他的平台上实现的。 C++ 中的 UB 是访问不活跃的工会成员。
        • 这与第一个答案有何不同?
        • @MartinJames:“似乎工作正常”是未定义行为的完美合法体现! :-) 你的圣经没有被触及...
        猜你喜欢
        • 2019-06-20
        • 2013-06-02
        • 2019-04-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多