[英]bitwise shifts, unsigned chars
任何人都可以详细解释这完成了什么? 我试图学习c并且很难绕过它。
void tonet_short(uint8_t *p, unsigned short s) {
p[0] = (s >> 8) & 0xff;
p[1] = s & 0xff;
}
void tonet_long(uint8_t *p, unsigned long l)
{
p[0] = (l >> 24) & 0xff;
p[1] = (l >> 16) & 0xff;
p[2] = (l >> 8) & 0xff;
p[3] = l & 0xff;
}
详细地说,这里是:
作为直接的答案; 它们都从左到右将变量的字节存储在一个字节数组中。 tonet_short
为unsigned short
变量执行此操作,该变量由2个字节组成; tonet_long
用于unsigned long
变量,由4个字节组成。
我将为tonet_long
解释它,而tonet_short
将只是你希望能够自己得出它的变体:
unsigned
变量,当它们的位被逐位移位时,使它们的位向确定的一侧移位以确定比特量,并使空出的位为0
,零。 即:
unsigned char asd = 10; //which is 0000 1010 in basis 2
asd <<= 2; //shifts the bits of asd 2 times towards left
asd; //it is now 0010 1000 which is 40 in basis 10
请记住,这是一个unsigned
变量,而这些可能是不正确的signed
的变量。
按位和&
操作者在两侧的两个操作数的位进行比较,返回1
(真),如果两者都是1
(真),和0
(假)如果它们中的任何一个或两者都是0
(假); 它为每一位做到这一点。 例:
unsigned char asd = 10; //0000 1010
unsigned char qwe = 6; //0000 0110
asd & qwe; //0000 0010 <-- this is what it evaluates to, which is 2
现在我们知道了按位移位和按位 - 并且,让我们到达函数tonet_long
的第一行:
p[0] = (l >> 24) & 0xff;
这里,由于l
是unsigned long
,则(l >> 24)
将被评估为第一4 * 8 - 24 = 8
的可变的比特l
,这是第一个字节的l
。 我可以像这样想象这个过程:
abcd efgh ijkl mnop qrst uvwx yz.. .... //letters and dots stand for
//unknown zeros and ones
//shift this 24 times towards right
0000 0000 0000 0000 0000 0000 abcd efgh
注意我们不改变l
,这只是l >> 24
的评估,这是暂时的。
然后,以十六进制(基数16)为0000 0000 0000 0000 0000 0000 1111 1111
的0xff
以逐位移位的l
进行逐位求和。 它是这样的:
0000 0000 0000 0000 0000 0000 abcd efgh
&
0000 0000 0000 0000 0000 0000 1111 1111
=
0000 0000 0000 0000 0000 0000 abcd efgh
因为a & 1
将完全依赖于a
,所以它将是a
; 其余的一样......看起来这是一个多余的操作,而且确实如此。 然而,对其他人来说,这一点很重要。 这是因为,例如,当您评估l >> 16
,它看起来像这样:
0000 0000 0000 0000 abcd efgh ijkl mnop
由于我们只需要ijkl mnop
部分,我们必须丢弃abcd efgh
,这将在0000 0000
的帮助下完成, 0xff
在其相应的位上。
我希望这会有所帮助,剩下的就像它到目前为止一样,所以......是的。
这些例程将16位和32位值从本机字节顺序转换为标准网络(大端)字节顺序。 它们通过从原生值移位和屏蔽8位块并将它们按顺序存储到字节数组中来工作。
如果我看到它正确,我基本上切换短和长的字节顺序...(反转数字的字节顺序)并将结果存储在希望有足够空间的地址:)
explain verbosely
- 好的......
void tonet_short(uint8_t *p, unsigned short s) {
short
通常是16位值(最大值:0xFFFF)
uint8_t
是无符号的8位值, p
是指向某些无符号8位值的指针(来自我们假设至少2个连续值的代码)。
p[0] = (s >> 8) & 0xff;
这将取s
值的“上半部分”并将其放在数组p
的第一个元素中。 所以我们假设s==0x1234
。
首先将s
移位8位( s >> 8 == 0x0012
)
那么它与0xFF
AND运算,结果存储在p[0]
。 ( p[0] == 0x12
)
p[1] = s & 0xff;
现在请注意,当我们执行该移位时,我们从未更改s
的原始值,因此s
仍然具有0x1234
的原始值,因此当我们执行第二行时,我们只是执行另一个逐位AND并且p[1]
得到s
的值的“下半部分”( p[0] == 0x34
)
这同样适用于你在那里的其他功能,但它是一个long
而不是短,所以我们假设在这种情况下p
有足够的空间用于所有32位(4x8),我们也必须做一些额外的移位。
此代码用于将16位或32位数字序列化为字节( uint8_t
)。 例如,将它们写入磁盘,或通过网络连接发送它们。
16位值分为两部分。 一个包含最高有效(高)8位,另一个包含最低有效(低)8位。 首先存储最重要的字节,然后存储最不重要的字节。 这称为大端或“网络”字节顺序。 这就是函数名为tonet_
的原因。
对于32位值的四个字节也是如此。
& 0xff
操作实际上是无用的。 当16位或32位值转换为8位值时,低8位( 0xff
)被隐式屏蔽。
位移用于将所需字节移动到最低8位。 考虑32位值的位:
AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD
最重要的字节是名为A
的8位。 为了将它们移动到最低的8位,该值必须右移24。
功能的名称是一个很大的提示......“净短”和“净长”。
如果你考虑十进制...说我们有两张纸如此之小我们只能在每一张纸上写一个数字,因此我们可以用它们来记录从0到99:00,01,02的所有数字。 .. 08,09,10,11 ...... 18,19,20 ...... 98,99。基本上,一张纸上有“十位”栏(假设我们的十进制数为10),其他的“单位”。
内存的工作方式类似于每个字节可以存储0..255的数字,所以我们在256的基数上工作。如果你有两个字节,其中一个将是“二百五十六”列,另一个是“单位”列。 要计算出组合值,您将前者加倍256并添加后者。
在纸面上,我们在左边用更重要的数字写数字,但是在计算机上不清楚更重要的值是应该在更高还是更低的内存地址中,因此不同的CPU制造商选择了不同的约定。
因此,一些计算机存储258 - 其为1 * 256 + 2 - 低= 1高= 2,而其他计算机存储低= 2高= 1。
这些函数的作用是将内存从CPU正常使用的内容重新排列为可预测的顺序 - 即,更重要的值进入较低的内存地址,最终将“单位”值放入最高的内存地址。 这是存储适用于所有计算机类型的数字的一致方式,因此当您想通过网络传输数据时,它非常棒; 如果接收计算机对base-256数字使用不同的内存排序,它可以将它们从网络字节顺序移动到它喜欢的任何顺序,然后将它们解释为CPU本机数字。
因此,“to net short”将最重要的8位s
打包到p[0]
- 较低的内存地址。 它实际上并不需要& 0xff
因为在取16个输入位并将它们8移到“右”之后,所有左手8位都保证为0,这是来自& 0xFF
的影响 - 例如:
1010 1111 1011 0111 // = decimal 10*256^3 + 15*256^2 + 11*256 + 7
>>8 0000 0000 1010 1111 // move right 8, with left-hand values becoming 0
0xff 0000 0000 1111 1111 // we're going to and the above with this
& 0000 0000 1010 1111 // the bits that were on in both the above 2 values
// (the and never changes the value)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.