使用指针在struct中的元素之间移动

使用指针在struct中的元素之间移动,c,memory,struct,C,Memory,Struct,这与经济发展密切相关 我正在写一些USB代码使用。查看库的源代码,我发现它们使用指针解析数据并填充structs 例如: 从libusb.h: struct libusb_endpoint_descriptor { uint8_t bLength; uint8_t bDescriptorType; uint8_t bEndpointAddress; uint8_t bmAttributes; uint16_t wMaxPacketSize;

这与经济发展密切相关

我正在写一些USB代码使用。查看库的源代码,我发现它们使用指针解析数据并填充
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;
};
描述符.c

struct libusb_endpoint_descriptor *endpoint
unsigned char *buffer
int host_endian

usbi_parse_descriptor(buffer, "bbbbwbbb", endpoint, host_endian);
其中,
usbi\u parse\u描述符
为:

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);
}
  • 这会发生什么

    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
    ,上面的语句会做什么

  • 不确定,我不会打赌标准能保证它,但至少在大多数PC平台上它应该能工作
  • dp+=((uintpttr)dp&1)
    dp
    四舍五入到2的下一个倍数。如果
    dp
    已为偶数,则该语句无效
  • 不确定,我不会打赌标准能保证它,但至少在大多数PC平台上它应该能工作
  • dp+=((uintpttr)dp&1)
    dp
    四舍五入到2的下一个倍数。如果
    dp
    已为偶数,则该语句无效

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


    这个表达式的有趣之处在于,它将导致
    0
    1
    成为结果,这取决于位是否未设置或是否已设置。如果未设置位,则将
    0
    添加到已对齐的16位地址,不会导致任何更改。另一方面,如果地址没有16位对齐,则表达式会将
    1
    添加到地址中,自动将其对齐到16位边界。

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


    这个表达式的有趣之处在于,它将导致
    0
    1
    成为结果,这取决于位是否未设置或是否已设置。如果未设置位,则将
    0
    添加到已对齐的16位地址,不会导致任何更改。另一方面,如果地址没有16位对齐,则表达式会将
    1
    添加到地址,并自动将其对齐到16位边界。

    要解决第一个问题,
    dest
    指向要写入的某个内存
    void*
    告诉您没有声明特定的类型,它只是一块内存。
    开关
    块测试预期的内容,并将指针增加一个字节或两个字节。这样做是安全的,因为
    无符号字符
    保证在
    sizeof(char)
    处对齐,即
    1


    编辑:不安全的是,在这种情况下,
    dest
    指向一个
    libusb\u端点\u描述符
    ,其对齐假设用
    描述符
    表示。这些假设取决于无法保证的预期。这里的代码可能依赖于编译器选项进行打包。

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


    编辑:不安全的是,在这种情况下,
    dest
    指向一个
    libusb\u端点\u描述符
    ,其对齐假设用
    描述符
    表示。这些假设取决于无法保证的预期。这里的代码可能依赖于编译器选项进行打包。

    部分答案:dp+=((uintpttr_t)dp&1);/*Align to word boundary*/将dp视为指针的整数表示形式(使用计算机上适当的大小来表示指针)。如果指针指向奇数地址,则会向dp添加1。这样做可以使dp在16位边界上对齐。继续,如果dp指向var1,语句将保持dp不变。如果它指向&var2,则语句将dp移动到&var3。实际上,它将dp移动到var2的第二个字节,而不是var3(var2未对齐)。部分答案:dp+=((uintptr_t)dp&1);/*Align to word boundary*/将dp视为指针的整数表示形式(使用计算机上适当的大小来表示指针)。如果指针指向奇数地址,则会向dp添加1。这样做可以使dp在16位边界上对齐。继续,如果dp指向var1,语句将保持dp不变。如果它指向&var2,则语句将dp移动到&var3实际上它将dp移动到var2的第二个字节,而不是var3(var2未对齐)。因此
    uint16\u t
    始终与16位地址对齐?这是吗
    ADDRESS  TYPE      NAME
    0x000a0  uint8_t   var1
    0x000a1  uint16_t  var2
    0x000a3  uint8_t   var3