在我发现的一些假设的C ++代码中,我将buffer
定义为const void *buffer;
(我认为它是任意二进制数据,被解释为32位无符号整数流)并且很多地方,我有
*(uint32_t *) &buffer[index]
其中index
是某种整数(我认为它是long
或unsigned long
并且在用int32_t
和uint32_t
取代之后被扫地出门当我在64位系统上运行代码时。)
我认识到这是取buffer
(&buffer
)的地址,将其作为指向uint32_t
的指针,并取消引用它,至少基于this question } ...但后来我对[index]
部分如何与之交互或我错过在我列出的步骤之间插入[index]
部分感到困惑。
从概念上讲,这是做什么的?有没有什么方法可以将另一个变量定义为更好的类型,在那里进行一次转换,然后使用它,而不是在整个代码中使用这个复杂的表达式?这实际上是C ++还是这个C99?
编辑: 代码的前几行是:
const void *buffer = data.bytes;
if (ntohl(*(int32_t *) buffer) != 'ttcf') {
return;
}
uint32_t ttf_count = ntohl(*(uint32_t *) &buffer[0x08]);
其中data.bytes
的类型为const void *
。在我从buffer
获取data.bytes
之前,它是char *
。
编辑2: 显然,让const void *buffer
工作不正常C(尽管它在我的情况下绝对有效),所以如果它更有意义,假设它是const char *buffer
。
答案 0 :(得分:4)
设置括号以使操作顺序更明确:
*((uint32_t *) &(buffer[index]))
因此,您将buffer
视为数组,但由于buffer
是void *
,因此您无法直接取消引用它。
假设您想将此缓冲区视为uint32_t
的数组,您要做的是:
((uint32_t *)buffer)[index]
也可以写成:
*((uint32_t *)buffer + index)
编辑:
如果index
是缓冲区中的字节偏移量,则会改变一些事情。在这种情况下,我建议将缓冲区定义为const char *
而不是const void *
。这样,您可以确保阵列的解除引用正常工作。
所以打破表达方式:
*(uint32_t *) &buffer[index]
您将index
个字节转到buffer
:buffer[index]
然后取该字节的地址:&buffer[index]
然后将该地址转换为uint32_t
:(uint32_t *) &buffer[index]
然后取消引用uint32_t
值:*(uint32_t *) &buffer[index]
答案 1 :(得分:2)
这里有很多问题!首先,void *
无法取消引用。 buffer[index]
在ISO C中是非法的,但有些编译器显然有一个扩展名,将其视为(void)((char *)buffer)[index]
。
您在评论中建议最初使用的代码char *
- 我建议您保留这种方式。假设buffer
返回const char *
:
if (ntohl(*(int32_t *) buffer) != 'ttcf') { return; }
这里的目的是假装buffer
的前四个字节包含一个整数;读取该整数,并将其与'ttcf'
进行比较。后者是一个多字节字符常量,其行为是实现定义的。它可以代表四个字符't', 't', 'c', 'f'
或'f', 'c', 't', 't'
,或者实际上代表int
类型的任何其他字符。
更大的问题是假装缓冲区包含int
,当它实际上没有通过类型int
的表达式写入时违反严格别名规则。遗憾的是,这是旧代码中常见的技术,但即使从第一个C标准开始,它也会导致未定义的行为。如果使用执行基于类型的别名优化的编译器,则可能会破坏您的代码。
编写此代码以避免这两个问题的方法是:
if ( memcmp(buffer, "ttcf", 4) ) { return; }
后一行uint32_t ttf_count = ntohl(*(uint32_t *) &buffer[0x08]);
也存在类似问题。在这种情况下,毫无疑问最好的解决方案是:
uint32_t ttf_count;
memcpy(&ttf_count, buffer + 0x08, sizeof ttf_count);
ttf_count = ntohl(ttf_count);
正如评论中所讨论的,您可以使用内联函数来保持这种整洁。在我自己的代码中,我做了类似的事情:
static inline uint32_t be_to_uint32(void const *ptr)
{
unsigned char const *p = ptr;
return p[0] * 0x1000000ul + p[1] * 0x10000ul + p[2] * 0x100 + p[3];
}
以及以相反顺序读取字节的类似版本le_to_uint32
;然后我使用与输入格式相对应的那些而不是使用ntohl
。