【问题标题】:Converting an int into a 4 byte char array (C)将 int 转换为 4 字节 char 数组 (C)
【发布时间】:2011-04-16 14:29:09
【问题描述】:

嘿,我希望将用户输入的 int 转换为 4 个字节,并将其分配给字符数组。如何做到这一点?

例子:

将用户输入的 175 转换为

00000000 00000000 00000000 10101111


到目前为止,所有答案都有问题,转换 255 应该会导致 0 0 0 ff 尽管它打印为:0 0 0 ffffffff

unsigned int value = 255;   

buffer[0] = (value >> 24) & 0xFF;
buffer[1] = (value >> 16) & 0xFF;
buffer[2] = (value >> 8) & 0xFF;
buffer[3] = value & 0xFF;

union {
    unsigned int integer;
    unsigned char byte[4];
} temp32bitint;

temp32bitint.integer = value;
buffer[8] = temp32bitint.byte[3];
buffer[9] = temp32bitint.byte[2];
buffer[10] = temp32bitint.byte[1];
buffer[11] = temp32bitint.byte[0];

两者都导致0 0 0 ffffffff 而不是0 0 0 ff

另一个例子是 175,因为输入输出为 0, 0, 0, ffffffaf,而它应该只是 0, 0, 0, af

【问题讨论】:

  • 你的 buffer[] 是如何定义的?值大于 127 的无符号字节设置了最高有效位。当使用有符号字节时,这表示负数。您的 FFFFFFFF 看起来像是对 0xFF 字节的符号扩展解释。
  • 您应该使用 %hhx 格式来打印您的号码。
  • ..但请注意 %hhx 是 C99 添加的。
  • Nit:你不能保证 4 个字节对于保存一个 int 是必要的和足够的。你需要sizeof (int)字节。
  • 致那些可能遇到这篇文章的人:唯一正确的解决方案是在接受的答案中对无符号类型使用位移。你可以阅读那个并停在那里。它是最快和最便携的版本。 memcpy 在无符号类型上是可以的,如果所有类型都是无符号的并且已经考虑了字节序。 union 解决方案不好,它们是不可移植的 - 当您可以通过同样的努力获得可移植代码时,为什么还要编写不可移植代码?指针算术解决方案不好,它们可能很慢,绝对不可移植,并且经常调用不明确的行为。

标签: c


【解决方案1】:

可移植的方法(确保您在任何地方都能得到0x00 0x00 0x00 0xaf)是使用轮班:

unsigned char bytes[4];
unsigned long n = 175;

bytes[0] = (n >> 24) & 0xFF;
bytes[1] = (n >> 16) & 0xFF;
bytes[2] = (n >> 8) & 0xFF;
bytes[3] = n & 0xFF;

使用 unions 和 memcpy() 的方法在不同的机器上会得到不同的结果。


您遇到的问题是打印而不是转换。我假设您使用的是char 而不是unsigned char,并且您正在使用这样的行来打印它:

printf("%x %x %x %x\n", bytes[0], bytes[1], bytes[2], bytes[3]);

当任何小于int 的类型被传递给printf 时,它们被提升为int(或unsigned int,如果int 不能保存原始类型的所有值)。如果char 在您的平台上签名,则0xff 可能不适合该类型的范围,而是设置为-1(在2s 补码机器上具有0xff 的表示)。

-1 被提升为int,并在您的机器上将0xffffffff 表示为int,这就是您所看到的。

您的解决方案是要么实际使用unsigned char,要么在printf 语句中转换为unsigned char

printf("%x %x %x %x\n", (unsigned char)bytes[0],
                        (unsigned char)bytes[1],
                        (unsigned char)bytes[2],
                        (unsigned char)bytes[3]);

【讨论】:

  • @jacobnlsn:这是您打印方式的问题,请参阅更新。
  • ZerOne: & 是按位与,如果两个操作数中的对应位都是 1,则给出每个位为 1 的结果。a & 0xFF 然后给出最低 8 位为的结果与a中的最低8位相同,其余位均为零。
  • @rem:虽然不常见,但char 允许比 8 位更宽(一些 DSP 编程环境会出现这种情况),因此掩码只是确保在这种情况下发生正确的事情。对于常见的情况,无论如何它都会被优化出来。
  • @user143252:不过,这正是最初的问题所要求的。
  • @user143252:当然,但这不是问这个问题的人想要的。
【解决方案2】:

您想寻址 32 位 int 的各个字节吗?一种可能的方法是联合:

union
{
    unsigned int integer;
    unsigned char byte[4];
} foo;

int main()
{
    foo.integer = 123456789;
    printf("%u %u %u %u\n", foo.byte[3], foo.byte[2], foo.byte[1], foo.byte[0]);
}

注意:更正了 printf 以反映无符号值。

【讨论】:

  • 编辑了我的问题,以便您可以看到我在解决方案时遇到的问题。
  • 我会写 uint32_t 而不是 int 只是为了不用担心 32 位和 64 位机器。
  • 小心确保您没有处理未定义的行为。 stackoverflow.com/questions/11639947
【解决方案3】:

在您的问题中,您表示要将用户输入 175 转换为 00000000 00000000 00000000 10101111,即big endian字节序,也称为网络字节序

正如您在给出的“175”示例中所建议的那样,将无符号整数转换为大端无符号字符数组的一种最可移植的方法是使用 C 的 htonl() 函数(在标题 <arpa/inet.h> 中定义) Linux 系统)将您的 unsigned int 转换为大端字节顺序,然后使用 memcpy()(在 C 的标头 <string.h> 中定义,C++ 的 <cstring> 中定义)将字节复制到您的 char(或 unsigned char)数组中。

htonl() 函数接受一个无符号的 32 位整数作为参数(与 htons() 不同,它接受一个无符号的 16 位整数)并将其从主机字节顺序转换为网络字节顺序(因此首字母缩略词 Host TO Network Long 与 Host TO Network Short for htons),以无符号 32 位整数形式返回结果。这一系列函数的目的是确保所有网络通信都以大端字节顺序进行,以便所有机器都可以通过套接字相互通信而不会出现字节顺序问题。 (顺便说一句,对于大端机器,htonl()htons()ntohl()ntohs() 函数通常被编译为“无操作”,因为字节不需要翻转在通过套接字发送或接收它们之前,因为它们已经处于正确的字节顺序中)

代码如下:

#include <stdio.h>
#include <arpa/inet.h>
#include <string.h>

int main() {
    unsigned int number = 175;

    unsigned int number2 = htonl(number);
    char numberStr[4];
    memcpy(numberStr, &number2, 4);

    printf("%x %x %x %x\n", numberStr[0], numberStr[1], numberStr[2], numberStr[3]);

    return 0;
}

请注意,正如 caf 所说,您必须使用 printf 的 %x 格式说明符将字符打印为 unsigned 字符。

上面的代码在我的机器上打印0 0 0 af(一台x86_64机器,它使用小端字节顺序),它是十六进制的175。

【讨论】:

    【解决方案4】:

    你可以试试:

    void CopyInt(int value, char* buffer) {
      memcpy(buffer, (void*)value, sizeof(int));
    }
    

    【讨论】:

    • 你不应该使用char作为字节,使用uint8_t(void*)value 完全错误,应该是 &amp;value
    【解决方案5】:

    为什么在 C++ 中需要一个中间转换来 void * 因为 cpp 不允许指针之间的直接转换,所以你需要使用 reinterpret_cast 或强制转换为 void* 来做这件事。

    【讨论】:

      【解决方案6】:
      int a = 1;
      char * c = (char*)(&a); //In C++ should be intermediate cst to void*
      

      【讨论】:

      • 为什么需要在 C++ 中对void * 进行中间转换?
      【解决方案7】:

      转换的问题(它在最后给你一个 ffffff 的原因)是因为你的十六进制整数(你正在使用 & 二元运算符)被解释为已签名。将其转换为无符号整数,就可以了。

      【讨论】:

        【解决方案8】:

        int 相当于 uint32_tchar 相当于 uint8_t

        我将展示我如何解决客户端-服务器通信,将实际时间(4 个字节,以 Unix 纪元格式化)发送到一个 1 位数组中,然后在另一端重新构建它。 (注意:协议是发送 1024 字节)

        • 客户端

          uint8_t message[1024];
          uint32_t t = time(NULL);
          
          uint8_t watch[4] = { t & 255, (t >> 8) & 255, (t >> 16) & 255, (t >> 
          24) & 255 };
          
          message[0] = watch[0];
          message[1] = watch[1];
          message[2] = watch[2];
          message[3] = watch[3];
          send(socket, message, 1024, 0);
          
        • 服务器端

          uint8_t res[1024];
          uint32_t date;
          
          recv(socket, res, 1024, 0);
          
          date = res[0] + (res[1] << 8) + (res[2] << 16) + (res[3] << 24);
          
          printf("Received message from client %d sent at %d\n", socket, date);
          

        希望对你有帮助。

        【讨论】:

          【解决方案9】:

          您可以简单地使用memcpy,如下所示:

          unsigned int value = 255;
          char bytes[4] = {0, 0, 0, 0};
          memcpy(bytes, &value, 4);
          

          【讨论】:

            【解决方案10】:

            问题出现了,因为 unsigned char 是一个 4 字节的数字,而不是许多人认为的 1 字节的数字,所以将其更改为

            union {
            unsigned int integer;
            char byte[4];
            } temp32bitint;
            

            并在打印时强制转换,以防止提升为 'int'(默认情况下 C 会这样做)

            printf("%u, %u \n", (unsigned char)Buffer[0], (unsigned char)Buffer[1]);
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2020-03-01
              • 1970-01-01
              • 2014-03-02
              • 2016-08-13
              • 1970-01-01
              • 2011-09-16
              • 2013-05-02
              • 1970-01-01
              相关资源
              最近更新 更多