有一个来自官方Quake 2源代码的代码摘录:
unsigned *buf;
dheader_t header;
...
header = *(dheader_t *)buf; // #1
for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
((int *)&header)[i] = LittleLong ( ((int *)&header)[i]); // #2
有人可以尽可能详细地解释我#1和#2行到底做了什么,因为我有点困惑或更困惑......
附言
如果有帮助,以下是其余的定义:
int LittleLong (int l) {return _LittleLong(l);}
...
typedef struct
{
int ident;
int version;
lump_t lumps[HEADER_LUMPS];
} dheader_t;
附言2
如果需要,我已经在原始的完整源代码代码上方链接。
这是一些非常脆弱的代码,你不应该写这样的代码。
它所做的是通过int
int
结构,然后对_LittleLong
中的每个此类int
执行一些操作。此函数很可能执行从大端整数到小端序的 32 位转换。这意味着您正在查看的来源可能与接收 IP 包有关。
逐步检查代码的作用:
for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
是一种草率的写法sizeof(dheader_t)/sizeof(int)
.即:遍历结构体int
int
,32 位的块。(int *)&header
从dheader_t*
转换为int*
。这实际上是由 C 中的一个特殊规则很好地定义的,该规则允许我们从指向结构的指针转换为指向其第一个成员的指针,反之亦然,第一个成员是int
.- 但是,这样做仅针对第一个成员进行了明确定义。相反,他们采用转换后的
int*
并对其应用数组取消引用:((int *)&header)[i]
.这是 C 语言中未定义的行为,即所谓的严格混叠冲突,在某些情况下还可能导致对齐问题。坏。 - 然后,通过此取消引用从结构中读取的
int
传递给LittleLong
,这很可能执行大>小端转换。 ((int *)&header)[i] =
,在这里它被写回它被抓取的地方。
更好、更安全、定义明确且可能更快的代码可能如下所示:
void endianify (dheader_t* header)
{
_Static_assert(sizeof(dheader_t)%sizeof(uint32_t)==0,
"Broken struct: dheader_t");
unsigned char* start = (unsigned char*)header;
unsigned char* end = start + sizeof(dheader_t);
for(unsigned char* i=start; i!=end; i+=sizeof(uint32_t))
{
uint32_t tmp;
memcpy(&tmp,i,sizeof(uint32_t));
i[0]= (tmp >> 24) & 0xFF;
i[1]= (tmp >> 16) & 0xFF;
i[2]= (tmp >> 8) & 0xFF;
i[3]= (tmp >> 0) & 0xFF;
}
}
拆卸:
endianify:
mov eax, DWORD PTR [rdi]
bswap eax
mov DWORD PTR [rdi], eax
mov eax, DWORD PTR [rdi+4]
bswap eax
mov DWORD PTR [rdi+4], eax
mov eax, DWORD PTR [rdi+8]
bswap eax
mov DWORD PTR [rdi+8], eax
mov eax, DWORD PTR [rdi+12]
bswap eax
mov DWORD PTR [rdi+12], eax
mov eax, DWORD PTR [rdi+16]
bswap eax
mov DWORD PTR [rdi+16], eax
ret