将int转换为4字节char数组(C)


77

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

例:

将用户输入175转换为

00000000 00000000 00000000 10101111


到目前为止,所有答案都存在问题,0 0 0 ff尽管输出为255,但应该转换为255 :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


6
您的buffer []如何定义?值大于127的无符号字节的最高位设置为1。使用带符号字节时,这表示负数。您的FFFFFFFF看起来像是0xFF字节的符号扩展解释。
高炉

您应该使用%hhx格式来打印数字。
詹斯·古斯特

..但请注意,这%hhx是C99的补充。
caf 2010年

1
Nit:您不能保证4个字节足以容纳一个int。您需要sizeof (int)字节。
pmg 2010年

对于可能会遇到此问题的人员:唯一正确的解决方案是,如接受的答案中那样,对无符号类型使用位移。您可以阅读该内容并在那里停下来。它是最快,最便携的版本。memcpy如果所有类型都是无符号的并且考虑了字节顺序,则在无符号的类型上就可以了。联合解决方案不是很好,它们是不可移植的-为什么当您以相同的努力获得可移植的代码时,为什么编写不可移植的代码?指针算术解决方案不可行,它们可能很慢,绝对不可移植,并且经常调用指定不正确的行为。
隆丁

Answers:


150

做到这一点(确保您无所不在)的可移植方法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;

使用联合的方法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(0xff在2s补码机上具有表示形式)。

-1被提升为int,并在您的计算机上具有的表示0xffffffff形式int,这就是您所看到的。

您的解决方案是实际使用unsigned char,或unsigned charprintf语句中强制转换为:

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

编辑了我的问题,这样您就可以看到我的解决方案存在的问题。
雅各布·尼尔森

2
@jacobnlsn:打印方式有问题,请参阅更新。
caf 2010年

您能解释一下'&0xFF;' 请一点吗?该代码的作用是什么?
ZerOne

1
ZerOne:&按位与运算,如果两个操作数中的对应位均为1,a & 0xFF则每个位为1, 然后给出结果,其中最低8位与中的最低8位相同a,其余位为全部为零。
caf 2013年

4
@rem:尽管不常见,但char允许宽度大于8位(某些DSP编程环境显示此宽度),因此掩码仅可确保在这种情况下正确执行操作。对于一般情况,无论如何都会对其进行优化。
caf

15

您是否要寻址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以反映无符号值。


编辑了我的问题,这样您就可以看到我的解决方案存在的问题。
Jacob Nelson

5
我写uint32_t而不是int只是不必担心32位和64位计算机。
Tadeusz A.Kadłubowski10年

1
注意确保您没有处理未定义的行为。stackoverflow.com/questions/11639947
bzeaman 2014年

7

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

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

htonl()函数接受一个无符号的32位整数作为参数(与相比htons(),接受一个无符号的16位整数)并将其从主机字节顺序转换为网络字节顺序(因此首字母缩写为Host TO Network Long, vs Host TO Network的缩写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格式说明符无符号字符。

上面的代码0 0 0 af在我的机器(x86_64机器,使用小尾数字节顺序)上打印,十六进制表示175。


2

你可以试试:

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

2
您不应该使用char字节,请使用uint8_t(void*)value是完全错误的,应该是&value
隆丁

1

为什么需要在C ++中将中间类型强制转换为void *,因为cpp不允许指针之间直接转换,所以您需要使用reinterpret_cast或强制转换为void *。


0
int a = 1;
char * c = (char*)(&a); //In C++ should be intermediate cst to void*

3
为什么需要void *在C ++中进行中间转换?
AnT 2010年

-1

转换的问题(最后给您ffffff的原因)是因为您的十六进制整数(使用了&二进制运算符)被解释为带符号。将其强制转换为无符号整数,就可以了。


-1

一个int相当于uint32_tcharuint8_t

我将展示如何解决客户端与服务器之间的通信,如何以1位数组发送实际时间(4个字节,以Unix纪元格式),然后在另一端重新构建它。(注意:该协议将发送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);
    

希望能帮助到你。


-2

由于无符号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]);

-2

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

unsigned int value = 255;
char bytes[4] = {0, 0, 0, 0};
memcpy(bytes, &value, 4);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.