Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/65.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C [0]=addr&;0xff?_C_Arrays_Bit Manipulation - Fatal编程技术网

C [0]=addr&;0xff?

C [0]=addr&;0xff?,c,arrays,bit-manipulation,C,Arrays,Bit Manipulation,我目前正在学习《shellcoder手册》,我对c有很强的理解,但最近我遇到了一段我无法掌握的代码 下面是一段代码: char a[4]; unsigned int addr = 0x0806d3b0; a[0] = addr & 0xff; a[1] = (addr & 0xff00) >> 8; a[2] = (addr & 0xff0000) >&

我目前正在学习《shellcoder手册》,我对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;
    

    不再需要屏蔽,因为所有不必要的信息都被移位丢弃。

    该代码有效地将32位地址存储在4个字符长的数组中。您可能知道,字符有一个字节(8位)。它首先复制地址的第一个字节,然后移位,复制第二个字节,然后移位,等等。您可以了解要点。

    它强制执行,并将整数以小端格式存储在
    a

    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 */
    

    请参阅wikipedia上的。

    代码试图在数据输入上强制使用endianness。具体来说,它试图在数据上强制执行little endian行为。以下是解释:

    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 */
    

    因此,基本上,代码是屏蔽和分离数据的每个字节,并以little-endian格式将其存储在数组“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" );
    

    除了给出的多个答案外,代码还有一些缺陷需要修复,以使代码具有可移植性。特别是,
    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;
    
    #包括
    uint8_t a[4];
    uint32\u t addr=0x0806d3b0UL;
    a[0]=addr&0xFFu;
    a[1]=(地址>>8)&0xFFu;
    a[2]=(地址>>16)&0xFFu;
    a[3]=(地址>>24)&0xFFu;
    

    严格地说,掩码
    &0xFFu
    是不需要的,但它们可能会使您避免一些关于错误整数类型的假阳性编译器警告。或者,可以将每个移位结果转换为
    uint8\u t
    ,这样也可以。

    (addr)>>X
    不是除以
    8
    。在某些情况下,这可能是正确的,但是
    >
    addr
    的右移,按
    X
    给出的字节数。这里实际需要屏蔽吗?隐式转换为
    char
    会不会不考虑这个问题?(顺便说一句,我还建议使用显式的
    无符号字符a[4]
    )@Jongware是的,隐式操作也可以。是的,使用
    unsigned char
    (或者更具体地说是
    uint8\u t
    )将是一个好主意。实际上,如果假设
    char
    的宽度为8位,则可以不使用掩蔽。(C标准规定它至少需要8位宽。)屏蔽使代码可移植到
    char
    大于8位的平台。是的,
    unsigned char
    更好。好吧,我明白了,但我们如何知道地址的哪一部分会进入我们的a[1],例如,没有掩码?非常感谢您修复我的代码,但有些事情我不明白(我不喜欢不明白),0xFFu掩码是什么?如何删除旧掩码?我这样问是因为你显然非常擅长c编程,我非常渴望了解更多关于c的知识,尤其是这类知识。@Th3Meg4ph0re旧掩码的类型是
    int
    ,这没有任何意义。不同之处在于,它们在换档之前屏蔽了
    int
    。现在假设
    addr
    也是一个
    int,您将在有符号类型上得到右移。如果符号位的位置碰巧有任何位,那么代码可能会遭到破坏,因为负数上的右移在C中没有很好的定义。您可能会得到算术或逻辑移位,或者其他一些。整数文字后的
    u`或u后缀确保它是无符号类型。@Th3Meg4ph0re至于移位前或移位后的掩蔽,其实并不重要,只要两个操作数都是无符号的。在小型CPU(如8位和16位)上,掩蔽后更有效,因为它可以用8位指令而不是32位指令完成。更重要的是,事后掩蔽使代码看起来更整洁。您甚至可以将第一行写为
    a[0]=(addr>>0)&0xFFu
    如果您希望它排列得更整齐,它将产生等效的机器代码。@Th3Meg4ph0re因为班次已经确定您感兴趣的部分现在位于最低有效字节。@Th3Meg4ph0re我很久没有读过任何C类书籍了,我唯一的建议是买一本至少涵盖C99标准的书。避免使用旧书,尤其是K&R.A
    #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;