使用指针在struct中的元素之间移动
这与经济发展密切相关 我正在写一些USB代码使用。查看库的源代码,我发现它们使用指针解析数据并填充使用指针在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;
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
,上面的语句会做什么
dp+=((uintpttr)dp&1)代码>将dp
四舍五入到2的下一个倍数。如果dp
已为偶数,则该语句无效
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