a[0] = addr & 0xff 是什么意思?
what does a[0] = addr & 0xff?
我目前正在学习"the shellcoder's handbook"这本书,我对c的理解很深,但最近我遇到了一段我无法掌握的代码。
这是一段代码:
char a[4];
unsigned int addr = 0x0806d3b0;
a[0] = addr & 0xff;
a[1] = (addr & 0xff00) >> 8;
a[2] = (addr & 0xff0000) >> 16;
a[3] = (addr) >> 24;
所以问题是这是做什么的,什么是 addr & 0xff(以及它下面的三行)以及是什么使 >> 8 成为它(我知道它将 8 除以 2)?
Ps:如果您对我应该使用的标签有任何想法,请随时告诉我。
变量addr
是32位数据,而数组a
中的每个元素都是8位。代码所做的是将 addr
的 32 位复制到数组 a
中,一次一个字节。
让我们走这条线:
a[1] = (addr & 0xff00) >> 8;
然后一步一步来
addr & 0xff00
这样就得到了addr
中的值的第8位到第15位,运算后的结果是0x0000d300
.
>> 8
这会将位向右移动,因此 0x0000d300
变为 0x000000d3
.
- 将掩码的结果值赋值并转移到
a[1]
。
显然,代码将各个字节与 addr
隔离开来,将它们存储在数组 a
中,以便对它们进行索引。第一行
a[0] = addr & 0xff;
通过使用0xff
作为位掩码来屏蔽掉最低值的字节;后续行做同样的事情,但另外将结果移到最右边的位置。最后,最后一行
a[3] = (addr) >> 24;
不再需要屏蔽,因为所有不必要的信息都被转换丢弃了。
unsigned char a[4]; /* I think using unsigned char is better in this case */
unsigned int addr = 0x0806d3b0;
a[0] = addr & 0xff; /* get the least significant byte 0xb0 */
a[1] = (addr & 0xff00) >> 8; /* get the second least significant byte 0xd3 */
a[2] = (addr & 0xff0000) >> 16; /* get the second most significant byte 0x06 */
a[3] = (addr) >> 24; /* get the most significant byte 0x08 */
该代码有效地将 32 位地址存储在 4 个字符长的数组中。您可能知道,一个 char 有一个字节(8 位)。它首先复制地址的第一个字节,然后移位,复制第二个字节,然后移位,等等。你明白了要点。
它强制执行 endianness,并将整数以小端格式存储在 a
中。
参见维基百科上的 illustration。
代码试图对数据输入强制执行字节顺序。具体来说,它试图对数据强制执行小端行为。解释如下:
a[0] = addr & 0xff; /* gets the LSB 0xb0 */
a[1] = (addr & 0xff00) >> 8; /* gets the 2nd LSB 0xd3 */
a[2] = (addr & 0xff0000) >> 16; /* gets 2nd MSB 0x06 */
a[3] = (addr) >> 24; /* gets the MSB 0x08 */
所以基本上,代码屏蔽并分离出数据的每个字节,并以小端格式将其存储在数组 "a" 中。
另外,为什么不可视化位移结果..
char a[4];
unsigned int addr = 0x0806d3b0;
a[0] = addr & 0xff;
a[1] = (addr & 0xff00) >> 8;
a[2] = (addr & 0xff0000) >> 16;
a[3] = (addr) >> 24;
int i = 0;
for( ; i < 4; i++ )
{
printf( "a[%d] = %02x\t", i, (unsigned char)a[i] );
}
printf("\n" );
输出:
a[0] = b0 a[1] = d3 a[2] = 06 a[3] = 08
我除了给出的多个答案外,代码还有一些缺陷需要修复才能使代码可移植。特别是 char
类型用于存储值是非常危险的,因为它的实现定义了符号。非常经典的 C 错误。如果代码是从一本书上摘下来的,那么你应该带着怀疑的态度去读那本书。
在我们这样做的同时,我们还可以整理代码,使其过于显式以避免未来潜在的维护错误,删除整数文字的一些隐式类型提升等。
#include <stdint.h>
uint8_t a[4];
uint32_t addr = 0x0806d3b0UL;
a[0] = addr & 0xFFu;
a[1] = (addr >> 8) & 0xFFu;
a[2] = (addr >> 16) & 0xFFu;
a[3] = (addr >> 24) & 0xFFu;
掩码 & 0xFFu
严格来说不是必需的,但它们可能会使您免受一些关于错误整数类型的误报编译器警告。或者,每个班次结果都可以转换为 uint8_t
,这样也可以。
我目前正在学习"the shellcoder's handbook"这本书,我对c的理解很深,但最近我遇到了一段我无法掌握的代码。
这是一段代码:
char a[4];
unsigned int addr = 0x0806d3b0;
a[0] = addr & 0xff;
a[1] = (addr & 0xff00) >> 8;
a[2] = (addr & 0xff0000) >> 16;
a[3] = (addr) >> 24;
所以问题是这是做什么的,什么是 addr & 0xff(以及它下面的三行)以及是什么使 >> 8 成为它(我知道它将 8 除以 2)? Ps:如果您对我应该使用的标签有任何想法,请随时告诉我。
变量addr
是32位数据,而数组a
中的每个元素都是8位。代码所做的是将 addr
的 32 位复制到数组 a
中,一次一个字节。
让我们走这条线:
a[1] = (addr & 0xff00) >> 8;
然后一步一步来
addr & 0xff00
这样就得到了addr
中的值的第8位到第15位,运算后的结果是0x0000d300
.>> 8
这会将位向右移动,因此0x0000d300
变为0x000000d3
.- 将掩码的结果值赋值并转移到
a[1]
。
显然,代码将各个字节与 addr
隔离开来,将它们存储在数组 a
中,以便对它们进行索引。第一行
a[0] = addr & 0xff;
通过使用0xff
作为位掩码来屏蔽掉最低值的字节;后续行做同样的事情,但另外将结果移到最右边的位置。最后,最后一行
a[3] = (addr) >> 24;
不再需要屏蔽,因为所有不必要的信息都被转换丢弃了。
unsigned char a[4]; /* I think using unsigned char is better in this case */
unsigned int addr = 0x0806d3b0;
a[0] = addr & 0xff; /* get the least significant byte 0xb0 */
a[1] = (addr & 0xff00) >> 8; /* get the second least significant byte 0xd3 */
a[2] = (addr & 0xff0000) >> 16; /* get the second most significant byte 0x06 */
a[3] = (addr) >> 24; /* get the most significant byte 0x08 */
该代码有效地将 32 位地址存储在 4 个字符长的数组中。您可能知道,一个 char 有一个字节(8 位)。它首先复制地址的第一个字节,然后移位,复制第二个字节,然后移位,等等。你明白了要点。
它强制执行 endianness,并将整数以小端格式存储在 a
中。
参见维基百科上的 illustration。
代码试图对数据输入强制执行字节顺序。具体来说,它试图对数据强制执行小端行为。解释如下:
a[0] = addr & 0xff; /* gets the LSB 0xb0 */
a[1] = (addr & 0xff00) >> 8; /* gets the 2nd LSB 0xd3 */
a[2] = (addr & 0xff0000) >> 16; /* gets 2nd MSB 0x06 */
a[3] = (addr) >> 24; /* gets the MSB 0x08 */
所以基本上,代码屏蔽并分离出数据的每个字节,并以小端格式将其存储在数组 "a" 中。
另外,为什么不可视化位移结果..
char a[4];
unsigned int addr = 0x0806d3b0;
a[0] = addr & 0xff;
a[1] = (addr & 0xff00) >> 8;
a[2] = (addr & 0xff0000) >> 16;
a[3] = (addr) >> 24;
int i = 0;
for( ; i < 4; i++ )
{
printf( "a[%d] = %02x\t", i, (unsigned char)a[i] );
}
printf("\n" );
输出:
a[0] = b0 a[1] = d3 a[2] = 06 a[3] = 08
我除了给出的多个答案外,代码还有一些缺陷需要修复才能使代码可移植。特别是 char
类型用于存储值是非常危险的,因为它的实现定义了符号。非常经典的 C 错误。如果代码是从一本书上摘下来的,那么你应该带着怀疑的态度去读那本书。
在我们这样做的同时,我们还可以整理代码,使其过于显式以避免未来潜在的维护错误,删除整数文字的一些隐式类型提升等。
#include <stdint.h>
uint8_t a[4];
uint32_t addr = 0x0806d3b0UL;
a[0] = addr & 0xFFu;
a[1] = (addr >> 8) & 0xFFu;
a[2] = (addr >> 16) & 0xFFu;
a[3] = (addr >> 24) & 0xFFu;
掩码 & 0xFFu
严格来说不是必需的,但它们可能会使您免受一些关于错误整数类型的误报编译器警告。或者,每个班次结果都可以转换为 uint8_t
,这样也可以。