好的,在编写 C 代码的 15 年中,我从未见过这样的代码,也不知道它是如何工作的。它以一些 C99 代码为中心,其中多行代码以某种方式解析为整数值。它来自 libplist,特别是这里。好的,它从第 394 行开始,其中结构的 uint64_t 成员被分配了宏 UINT_TO_HOST 的结果
data->intval = UINT_TO_HOST(&data->intval, size);
UINT_TO_HOST 是一个宏,它接受一个指向无符号整数的指针,以及整数的字节大小。宏定义了一组不同 uint 大小的指针的联合,然后将地址设置为传递给宏的 uint 指针。然后它继续对齐内存中的 uint,以便正确地将其与另一个宏进行字节交换。UINT_TO_HOST 在第 172 行定义
#define UINT_TO_HOST(x, n) \
({ \
union plist_uint_ptr __up; \
__up.src = x; \
(n == 8 ? be64toh( get_unaligned(__up.u64ptr) ) : \
(n == 4 ? be32toh( get_unaligned(__up.u32ptr) ) : \
(n == 3 ? uint24_from_be( __up ) : \
(n == 2 ? be16toh( get_unaligned(__up.u16ptr) ) : \
*__up.u8ptr )))); \
})
get unaligned 使用 struct 和打包的 gcc 属性做了一个技巧,它返回基于类型在内存中对齐的指针值。
#define get_unaligned(ptr) \
({ \
struct __attribute__((packed)) { \
typeof(*(ptr)) __v; \
} *__p = (void *) (ptr); \
__p->__v; \
})
be64toh 只是做了一些就地掩蔽和移位。
#define be64toh(x) ((((x) & 0xFF00000000000000ull) >> 56) \
| (((x) & 0x00FF000000000000ull) >> 40) \
| (((x) & 0x0000FF0000000000ull) >> 24) \
| (((x) & 0x000000FF00000000ull) >> 8) \
| (((x) & 0x00000000FF000000ull) << 8) \
| (((x) & 0x0000000000FF0000ull) << 24) \
| (((x) & 0x000000000000FF00ull) << 40) \
| (((x) & 0x00000000000000FFull) << 56))
问题是 UINT_TO_HOST 如何实际将值返回到 data->intval,因为 UINT_TO_HOST 基本上被解析为一组花括号 {} 内的 3 行代码。get_unaligned 内部似乎也发生了同样的事情,大概是最后一条语句
__p->_v;
是什么被退回。这个功能叫什么,任何人都可以指出关于这个“功能”的一些文档吗?