2

这与这个问题息息相关。

我正在使用libusb. 查看库的源代码,我看到他们正在使用指针来解析数据和填充structs

例子:

来自libusb.h

struct libusb_endpoint_descriptor {
    uint8_t  bLength;
    uint8_t  bDescriptorType;
    uint8_t  bEndpointAddress;
    uint8_t  bmAttributes;
    uint16_t wMaxPacketSize;
    uint8_t  bInterval;
    uint8_t  bRefresh;
    uint8_t  bSynchAddress;
    const unsigned char *extra;
    int extra_length;
};

来自descriptor.c

struct libusb_endpoint_descriptor *endpoint
unsigned char *buffer
int host_endian

usbi_parse_descriptor(buffer, "bbbbwbbb", endpoint, host_endian);

哪里usbi_parse_descriptor是:

int usbi_parse_descriptor(
        unsigned char *source, 
        const char *descriptor,
        void *dest, 
        int host_endian)
{
    unsigned char *sp = source;
    unsigned char *dp = dest;
    uint16_t w;
    const char *cp;

    for (cp = descriptor; *cp; cp++) {
        switch (*cp) {
        case 'b':    /* 8-bit byte */
                *dp++ = *sp++;
                break;
        case 'w':    /* 16-bit word, convert from little endian to CPU */
            dp += ((uintptr_t)dp & 1);    /* Align to word boundary */
            if (host_endian) {
                memcpy(dp, sp, 2);
            } else {
                w = (sp[1] << 8) | sp[0];
                *((uint16_t *)dp) = w;
            }
            sp += 2;
            dp += 2;
            break;
        }
    }

    return (int) (sp - source);
}

我的问题是使用char指针来遍历缓冲区。

  1. 一个风险难道不能被编译器uint8_t对齐为一个整数,例如,一个整数——从而导致错误的地址吗?错误地址是指 struct 中变量的地址,它指向:uint32_t*dp++
    libusb_endpoint_descriptordp

    unsigned char *buffer = REPLY from request to USB device.
    struct libusb_endpoint_descriptor *endpoint;
    unsigned char *dp = (void*) endpoint;
    
      *dp = buffer[0] ==> struct libusb_endpoint_descriptor -> bLength
    *++dp = buffer[1] ==> struct libusb_endpoint_descriptor -> bDescriptorType
    ... v                                                         ^
        |                                                         |
        +--- does this guaranteed align with this ----------------+
    
  2. 这会发生什么?:

    dp += ((uintptr_t)dp & 1);    /* Align to word boundary */
    

如果结构在内存中是这样的:

ADDRESS  TYPE      NAME
0x000a0  uint8_t   var1
0x000a1  uint16_t  var2
0x000a3  uint8_t   var3

dp指向var1; 0x000a0,上面的语句会做什么?

4

3 回答 3

2

关于你的第二个问题,地址只是一个整数,它只是使用它来表示内存位置的编译器。表达式((uintptr_t)dp & 1)首先将其转换为适当的整数(该类型uintptr_t是一个足以容纳指针的整数),然后检查是否设置了最低有效位。如果该位未设置,则表达式的结果为零,这意味着地址是偶数且 16 位对齐。如果设置了该位,意味着地址不均匀并且不是 16 位对齐的。

这个表达式的有趣之处在于它会产生0或者1是结果,这取决于该位是否未设置。如果未设置该位,则将0其添加到已经 16 位对齐的地址中,从而不会发生任何变化。另一方面,如果地址不是 16 位对齐的,则表达式会1被添加到地址中,自动将其与 16 位边界对齐。

于 2013-05-23T13:55:15.610 回答
1
  1. 不确定,我不会打赌保证它的标准,但至少在大多数 PC 平台上它应该可以工作。
  2. dp += ((uintptr_t)dp & 1);向上舍dp入到 2 的下一个倍数。如果dp已经是偶数,则该语句无效。
于 2013-05-23T13:52:22.067 回答
1

为了解决您的第一个问题,请dest指向要写入的一些内存。void *告诉你没有声明特定的类型,它只是一块内存。该switch块测试预期的内容,并将指针增加一个或两个字节。这样做的方式是安全的,因为unsigned char保证在 对齐sizeof(char),即1.

编辑: 对此不安全的是,dest在这种情况下,它指向一个 libusb_endpoint_descriptor带有其对齐假设的descriptor. 这些假设取决于无法保证的填充期望。可能这里的代码依赖于编译器选项进行打包。

于 2013-05-23T14:06:50.393 回答