将无符号字符数组转换为有符号整数(反之亦然)

4
我将尝试将一个无符号字符数组缓冲区转换为有符号整数(反之亦然)。
下面是演示代码:
int main(int argv, char* argc[])
{
    int original = 1054;
    unsigned int i = 1054;
    unsigned char c[4];
    int num;

    memcpy(c, (char*)&i, sizeof(int));

    //num  = *(int*) c;                          // method 1 get
    memcpy((char *)&num, c, sizeof(int));        // method 2 get
    printf("%d\n", num);

    return 0;
}

1) 我应该使用哪种方法将 unsigned char[] 转换为 int?

是使用方法 1 还是方法 2? (或者有其他建议)

2) 如何将 int 转换为 unsigned char[]?

我需要通过仅接受 unsigned char[] 的缓冲区发送此整数。

目前我所做的是将 int 转换为 unsigned int,然后再转换为 char[],例如:

int g = 1054;
unsigned char buf[4];
unsigned int n;
n = g;
memcpy(buf, (char*)&n, sizeof(int));

虽然它能正常工作,但我不确定这是否是正确的方式或者是否安全?

附注:我正在尝试通过USB串行通信(在树莓派和Arduino之间)发送数据。


4
不确定是否安全,但为了使其更不危险,可以使用 unsigned char buf[sizeof(int)] 而不是 unsigned char buf[4] - user824425
2
如果字符数组没有正确对齐,方法1可能会导致段错误。 - phuclv
好的,谢谢所有的建议!我会坚持使用memcpy方法! - Doe Joe
1
*(int *)c 也违反了严格别名规则,请勿这样做。 - M.M
如果我没记错的话,char*void*是例外。 - user3528438
显示剩余2条评论
2个回答

4
无论机器的字节顺序如何(假设sizeof(int)==4),以下方法都可以使用:

unsigned char bytes[4];
unsigned int n = 45;

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

上述代码以小端方式将整数转换为字节数组。此链接here提供了更多信息。
要进行相反的操作,请参见这里的答案。
使用memcpy的方法可能在不同计算机上产生不同的结果。因为memcpy会将源地址中的所有内容复制到目标地址中,并且根据计算机是小端序还是大端序,可能会在源地址的起始位置有一个LSB或MSB。

2
虽然所有的 & 0xff 实际上并不是必要的(但它们可能会增加一些清晰度),但我还是点了赞。 - Matteo Italia
2
@某些情况下,联合操作符会因为类似于memcpy的原因在不同的机器上产生不同的结果。 - Giorgi Moniava
1
@Giorgi,只是好奇,为什么无论机器的字节序如何,小端方式都能正常工作?我尝试在安卓设备和树莓派上通过蓝牙进行测试,大端方式可以正常工作,而小端方式则不行。对于这些字节序问题真的很好奇。 - Doe Joe
2
@DoeJoe,不是小端工作而大端不工作,而是方法不同。我提到的方法,如果你使用小端方式对整数进行编码,并在另一台机器上应用类似的反向操作(从字节数组中恢复整数,假设字节以小端方式排列),你将得到相同的结果。即使你将整数转换为大端方式,这种方法也适用于任何机器。现在可能是你的设备期望以某种字节顺序接收整数,这是另外一回事。你可能需要对字节顺序进行更多的研究。 - Giorgi Moniava
@DoeJoe 您好,欢迎。请阅读有关字节序的信息,并确保您理解为什么这种方法是可移植的,而其他方法则不是。 - Giorgi Moniava
显示剩余4条评论

3
您可以将int(或unsigned int)和unsigned char数组都存储为union。这种方法被称为type punning,自C99标准以来已完全得到规范化(尽管在此之前这是常见做法)。假设sizeof(int) == 4
#include <stdio.h>

union device_buffer {
    int i;
    unsigned char c[4];
};

int main(int argv, char* argc[])
{
    int original = 1054;

    union device_buffer db;
    db.i = original;

    for (int i = 0; i < 4; i++) {
        printf("c[i] = 0x%x\n", db.c[i]);
    }
}

请注意,数组中的值是根据字节顺序存储的,即端序。

我在StackOverflow上看到有人说memcpy和union可能会产生不同的结果!这是真的吗?Union和Struct之间有什么区别?非常感谢! - Doe Joe
1
请注意,在C++中,通过联合进行类型游戏仍然是未定义行为;普遍安全的方法仍然是使用memcpy - Matteo Italia
@DoeJoe:char 是我的打字错误,现在已经更正了。至于你的第二个问题,只要你在同一台机器上运行代码,memcpy 和 union 的结果应该完全相同。 - Grzegorz Szpetkowski
我可以问一下你,我在将有符号整数放入缓冲区之前是否需要将其更改为无符号整数,或者完全没有必要? - Doe Joe
1
@DoeJoe:除非您存储的值超出了“int”的范围,否则这并不重要。无论如何,我改成了“unsigned”,因为这是您问题中提到的。 - Grzegorz Szpetkowski
2
如果int类型少于4个字符,则这是ub:db.i = original; 剩余的字符未初始化。 - user3528438

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接