在C语言中将字节转换为整数/无符号整数

29

我有一个unsigned char数组[248],里面存储了许多字节,例如2F AF FF 00 EB AB CD EF ......

这个数组是我从UART(RS232)接收数据时存储到缓冲区中的字节流。

现在我想把这些字节转换回我的uint16和int32。

在C#中,我使用BitConverter类来进行转换。例如:

byte[] Array = { 0A, AB, CD, 25 };
int myint1 = BitConverter.ToInt32(bytes, 0);
int myint2 = BitConverter.ToInt32(bytes, 4);
int myint3 = BitConverter.ToInt32(bytes, 8);
int myint4 = BitConverter.ToInt32(bytes, 12);
//...
enter code here
Console.WriteLine("int: {0}", myint1); //output Data...

在C语言中是否有类似的函数?(不使用 .net,我使用 KEIL 编译器,因为代码将在微控制器上运行)

谢谢 Sam


本网站采用问答格式,请勿编辑问题以包含答案。而且,您编辑到问题中的答案也是错误的。 - M.M
4个回答

44

在C语言中没有标准函数可以为您执行此操作。您将不得不自己将字节组装回您的16位和32位整数中。请注意字节序!

这是一个简单的小端示例:

extern uint8_t *bytes;
uint32_t myInt1 = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24);

对于大端系统,它的顺序正好相反:

uint32_t myInt1 = (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3];

你也许可以试试以下方法:

uint32_t myInt1 = *(uint32_t *)bytes;

如果你对齐问题很谨慎。


4
在进行移位操作之前,我会将每个 bytes[] 转换为 uint32_t 类型。如上所述,它们将在进行移位操作之前被转换为 int 类型,但 int 并不一定是 32 位。 - Joshua Green
那样做是最安全的,同意吗? - Carl Norum
假设“bytes”是本机字节序,你会遇到什么样的对齐问题?或者说,在什么情况下会出现对齐问题? - Azmisov
1
*(uint32_t *)bytes 会导致未定义行为(严格别名违规)。 - M.M
1
谢谢!最佳答案!你救了我的一天! - imike
显示剩余2条评论

20

是的,有这个功能。假设你的字节在:

uint8_t bytes[N] = { /* whatever */ };

我们知道,一个16位的整数只是两个8位整数连接在一起,也就是说其中一个整数乘以256或者移位8位:

uint16_t sixteen[N/2];

for (i = 0; i < N; i += 2)
    sixteen[i/2] = bytes[i] | (uint16_t)bytes[i+1] << 8;
             // assuming you have read your bytes little-endian

同样适用于32位:

uint32_t thirty_two[N/4];

for (i = 0; i < N; i += 4)
    thirty_two[i/4] = bytes[i] | (uint32_t)bytes[i+1] << 8
        | (uint32_t)bytes[i+2] << 16 | (uint32_t)bytes[i+3] << 24;
             // same assumption
如果字节是以大端方式读取的,当然你需要反转它们的顺序:
bytes[i+1] | (uint16_t)bytes[i] << 8

并且

bytes[i+3] | (uint32_t)bytes[i+2] << 8
    | (uint32_t)bytes[i+1] << 16 | (uint32_t)bytes[i] << 24

注意存储的整数字节序与运行体系结构的字节序之间存在差异。本答案中所指的字节序是存储的整数字节序,即bytes的内容。解决方案与运行体系结构的字节序无关,因为在进行移位操作时已经处理了字节序


这可能是一个愚蠢的问题,但我的Array[100]是一个char数组,这会有影响吗?因为你们写的是uint8_t bytes[N]。 - Sam
@sam,如果你的系统上char是有符号的,那么是有影响的。想象一下0xFF。如果你将其视为有符号数,那么它就是-1。所以,你会减去1而不是加上255,这不是你想要的结果。顺便说一下,unsigned charuint8_t是相同的。 - Shahbaz
我在尝试Ethan的方法时上传了我的Debug-Output http://s18.postimage.org/fipv7svp3/debugbyte.png。看起来其他字节不会填充...现在我正在尝试你的方法。 - Sam
是我自己的问题,还是只有我不得不使用位掩码才能使其正常工作。由于某种原因,我的4字节int类型的内部2个字节似乎在进行位移时会累积前导1。例如,假设小端字节位置二为E1,则(int)0xE1<<8计算结果为4294959360而不是57600。使用((int)0xE1<<8)&0x0000FF00似乎可以纠正它。我在这里忽略了什么? - Chad Harrison
@hydroparadise,我认为你没有说清楚。你的数字可能存储在一个char中,在你的系统中是有符号的,所以0xE1实际上是一个负数。将其转换为int会导致符号扩展保持有符号值不变。左移与此无关。 - Shahbaz
显示剩余8条评论

5

如果是小端模式,难道不能使用memcpy吗?

memcpy((char*)&myint1, aesData.inputData[startindex], length);

本地机器并不总是小端序。 - Daniel Chin

1
            char letter = 'A';
            size_t filter = letter;
            filter = (filter <<  8 | filter);
            filter = (filter << 16 | filter);
            filter = (filter << 32 | filter);
            printf("filter: %#I64x \n", filter); 

结果: "过滤器:0x4141414141414141"

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