簡體   English   中英

使用指針在結構中的元素之間移動

[英]Using pointers to move between elements in struct

這與這個問題密切相關。

我正在使用libusb編寫一些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;
};

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對齊為例如uint32_t整數的風險–這樣*dp++以錯誤的地址結尾?
    錯誤的地址是指struct 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 ,上面的語句會做什么?

關於第二個問題,地址只是一個整數,只是編譯器使用它來表示一個內存位置。 表達式((uintptr_t)dp & 1)首先將其轉換為適當的整數(類型uintptr_t是足以容納指針的整數),然后檢查是否設置了最低有效位。 如果未設置該位,則表達式的結果為零,這意味着該地址是偶數且16位對齊。 如果該位設置,這意味着該地址是不平衡的,這不是16位對齊。

關於此表達式的有趣之處在於,將根據結果是否為01來得出結果為01 如果未設置該位,則將0添加到已經為16位的對齊地址中,從而保持不變。 另一方面,如果地址不是16位對齊的,則表達式會導致將1添加到該地址,從而自動將其對齊到16位邊界。

  1. 不確定,我不會打賭要保證它的標准,但是至少在大多數PC平台上它應該能工作。
  2. dp += ((uintptr_t)dp & 1); dp向上dp入為2的下一個倍數。如果dp已經是偶數,則該語句無效。

為了解決您的第一個問題, dest指向要寫入的內存。 void *告訴您沒有聲明的特定類型,它只是一塊內存。 switch塊測試預期的結果,然后將指針增加一或兩個字節。 這樣做是安全的,因為保證unsigned char對齊為sizeof(char) ,即1

編輯 :對此不安全的是,在這種情況下, dest指向libusb_endpoint_descriptor並假定其在descriptor對齊。 這些假設取決於無法保證的填充期望。 可能此代碼在此依賴編譯器選項進行打包。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM