[英]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
指針遍歷緩沖區。
難道沒有編譯器將uint8_t
對齊為例如uint32_t
整數的風險–這樣*dp++
以錯誤的地址結尾?
錯誤的地址是指struct libusb_endpoint_descriptor
中dp
指向的變量的地址:
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 ----------------+
這會發生什么?:
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位對齊。
關於此表達式的有趣之處在於,將根據結果是否為0
或1
來得出結果為0
或1
。 如果未設置該位,則將0
添加到已經為16位的對齊地址中,從而保持不變。 另一方面,如果地址不是16位對齊的,則表達式會導致將1
添加到該地址,從而自動將其對齊到16位邊界。
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.