C 因特网校验和中的位移位
这几乎肯定是一个非常愚蠢的问题,但出于某种原因,我在互联网校验和计算方面遇到了麻烦。所有算法基本上如下所示:C 因特网校验和中的位移位,c,bit-manipulation,checksum,C,Bit Manipulation,Checksum,这几乎肯定是一个非常愚蠢的问题,但出于某种原因,我在互联网校验和计算方面遇到了麻烦。所有算法基本上如下所示: WORD chksm(WORD *startpos, WORD checklen){ ulong sum = 0; WORD answer = 0; while (checklen > 1) { sum += *startpos++; checklen -= 2; } if (checklen == 1) { *(BYTE *)(&answer)
WORD chksm(WORD *startpos, WORD checklen){
ulong sum = 0;
WORD answer = 0;
while (checklen > 1)
{
sum += *startpos++;
checklen -= 2;
}
if (checklen == 1)
{
*(BYTE *)(&answer) = *(BYTE *)startpos;
sum += answer;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return answer;}
我什么都清楚,除了那句话:
sum += (sum >> 16);
它看起来像是将前16位添加到下16位之前的一行,在前16位中保留所有零。如果是这样的话,那么现在的和>>16不是等于零吗?如果是这样的话,为什么会有这条线
或者我(很可能)今天只是精神完全衰竭 你几乎是对的
由于进位,高16位可能是1
例如,
FFFF+FFFF=>1ffe
,或者可能FFFF+1=>10000
,我认为ulong是32位宽的,这意味着:
sum = (sum >> 16) + (sum & 0xffff)
sum += (sum >> 16);
将顶部sicteen位和底部sisteen位相加。然后下一行对前16位的结果求和;由于进位运算,其中可能有一个1。这是1的补码和定义的一部分。您获取任何溢出位,并将其添加回较低的16位。将它们加回去可能会导致进一步的溢出,因此重复此操作,直到高位全部为零。所以,从概念上讲,是这样的:
while (sum >> 16 != 0) {
sum = (sum >> 16) + (sum & 0xffff);
}
但是,此循环最多只执行两次,因此不需要显式循环。在第一次加法之后,可能会或可能不会出现溢出,进位结束于上16位。在这种情况下,上面的16位将是0x0001
,您必须再做一次加法才能将进位添加回
想象一下最坏的情况,在初始while循环之后,sum结束为0xffffff
。然后,添加操作将按以下步骤进行:
sum = (0xffffffff >> 16) + (0xffffffff & 0xffff)
= 0xffff + 0xffff
= 0x1fffe
sum = (0x1fffe >> 16) + (0x1fffe & 0xffff)
= 0x1 + 0xfffe
= 0xffff
加上两个加法,我们就完成了,因为上面的16位现在已经清楚了。这是最坏的情况,因此循环可以展开为两个加法
(然后,你把最后一个和的一个补码取出来,导致这个名字非常混乱:一个补码和的一个补码。在我第一次实现它的时候,我花了很长时间才意识到这一点,特别是一个补码和不涉及~
补码歌剧。)托尔。)