我有这个C代码块,我无法理解我的生活。我需要为我发送给该方法的某个字节数组计算CRC-16,它应该给出msb(最高有效字节)和lsb(最低有效字节)。我还获得了一个C编写的应用程序来测试一些功能,该应用程序还为我提供了发送内容和通过COM端口接收的内容的日志。
奇怪的是我输入了我在日志this online calculator中找到的十六进制字符串,但它给了我不同的结果。
我尝试将方法翻译成C#,但我不了解某些方面:
以下是代码块:
unsigned short CalculateCRC(unsigned char* a_szBufuer, short a_sBufferLen)
{
unsigned short usCRC = 0;
for (short j = 0; j < a_sBufferLen; j++)
{
unsigned char* pucPtr = (unsigned char*)&usCRC;
*(pucPtr + 1) = *(pucPtr + 1) ^ *a_szBufuer++;
for (short i = 0; i <= 7; i++)
{
if (usCRC & ((short)0x8000))
{
usCRC = usCRC << 1;
usCRC = usCRC ^ ((ushort)0x8005);
}
else
usCRC = usCRC << 1;
}
}
return (usCRC);
}
这是我转换为字节数组并发送到方法的十六进制字符串: 02 00 04 a0 00 01 01 03
这是应该从CRC演算中得出的结果: 06 35
我收到的文件说这是整个数据的CRC16 IBM(msb,lsb)。
有人可以帮忙吗?我已经被困了一段时间了。
那里有能够将C方法转换为C#的代码大师吗?显然我不具备这样的资源。
答案 0 :(得分:5)
首先,请注意,在C中,^
运算符表示按位异或。
- pucPTR在那里做什么(它没有在其他任何地方使用过)?
- 2行代码的含义是什么,第一行代表什么?
醇>
通过它的外观导致错误。它仅用于获取FCS的两个字节中的一个,但代码是以依赖于endianess的方式编写的。
在处理校验和算法时,Endianess非常重要,因为它们最初是为硬件移位寄存器设计的,它们首先需要MSB,即大端。此外,CRC通常意味着数据通信,数据通信意味着发送方,协议和接收方之间可能有不同的字节顺序。
我猜这个代码只是为小端机器编写的,目的是用ms字节进行异或。代码指向第一个字节,然后使用+1指针算法到达第二个字节。更正后的代码应该是:
uint8_t puc = (unsigned int)usCRC >> 8;
puc ^= *a_szBufuer;
usCRC = (usCRC & 0xFF) | ((unsigned int)puc << 8);
a_szBufuer++;
对unsigned int的强制转换可以通过隐式整数提升来防止意外事件。
- 为什么在第二个短片&#34; i&#34;是&lt; = 7,不应该&lt; = 8?
醇>
我认为这是正确的,但更可读的是它可以写成i < 8
。
- if语句中的最后一行表示usCRC实际上是ushort 8005?
醇>
不,这意味着用多项式0x8005对您的FCS进行异或。请参阅this。
我收到的文件说这是CRC16 IBM
是的,它有时被称为。虽然我记得,&#34; CRC16 IBM&#34;还涉及最终结果的一些反转(?)。我仔细检查一下。
总的来说,请小心这段代码。谁写了它并没有很多关于endianess,整数签名和隐式类型促销的线索。这是业余级代码。您应该能够在网上找到相同CRC算法的更安全,便携的专业版本。
关于这个主题的非常好的阅读是A Painless Guide To CRC。
答案 1 :(得分:2)
- pucPTR在那里做什么(它没有在其他任何地方使用过)?
醇>
pucPtr
用于将 uglily unsigned short
转换为2 unsigned char
的数组。根据平台的结束,pucPtr
将指向unsigned short
的第一个字节,而pucPtr+1
将指向unsigned short
的第二个字节(反之亦然)。您必须知道此算法是针对小端还是大端设计的。
代码等价(如果代码是为big endian开发的,则是可移植的):
unsigned char rawCrc[2];
rawCrc[0] = (unsigned char)(usCRC & 0x00FF);
rawCrc[1] = (unsigned char)((usCRC >> 8) & 0x00FF);
rawCrc[1] = rawCrc[1] ^ *a_szBufuer++;
usCRC = (unsigned short)rawCrc[0]
| (unsigned short)((unsigned int)rawCrc[1] << 8);
对于小端,您必须反转raw[0]
和raw[1]
- 2行代码的含义是什么,第一行代表什么?
醇>
第一行执行1中描述的 丑陋 转换。
第二行检索a_szBufuer
指向的值并递增它。并且&#34; xor&#34;使用crc的第二个(或第一个,根据endianess)字节(注意*(pucPtr +1)
等同于pucPtr[1]
)并将结果存储在crc的第二个(或第一个,根据endianess)字节内。
*(pucPtr + 1) = *(pucPtr + 1) ^ *a_szBufuer++;
相当于
pucPtr[1] = pucPtr[1] ^ *a_szBufuer++;
- 为什么在第二个短片&#34; i&#34;是&lt; = 7,不应该&lt; = 8?
醇>
您必须进行8次迭代,从0到7.您可以将条件更改为i = 0; i<8
或i=1; i<=8
- if语句中的最后一行表示usCRC实际上是ushort 8005?
醇>
不,它没有。这意味着usCRC
现在等于usCRC
XOR 0x8005
。 ^
是XOR bitwise operation (也称为或排他)。例如:
0b1100110
^0b1001011
----------
0b0101101