好的,在编写C代码的15年中,我从未见过像这样的代码,我不知道它是如何工作的。它以一些C99代码为中心,以某种方式将多行代码解析为整数值。它来自libplist,特别是here。好的,它从第394行开始,其中一个结构的uint64_t成员被赋予宏的结果UINT_TO_HOST
data->intval = UINT_TO_HOST(&data->intval, size);
UINT_TO_HOST是一个宏,它接受一个指向无符号整数的指针,以及整数的btes大小。宏定义了一组不同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 )))); \
})
得到未对齐做一个结构和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-&gt; intval,因为UINT_TO_HOST基本上被解析为一组花括号{}内的3行代码。同样的事情似乎发生在get_unaligned里面,可能是最后一个声明
__对 - &GT; _v;
返回的是什么。这个功能叫做什么,任何人都可以指出这个'功能'的一些文档吗?
答案 0 :(得分:4)
这称为语句表达式,它是GNU扩展。 Here's您可能想要阅读的一些文档。
答案 1 :(得分:0)
正如其他人所指出的,宏使用GCC语法“表达式中的语句”。
但宏观本身并不是很好。
小心点。该宏的代码可以被破坏,因为内部存在{ }
例如:
union plist_uint_ptr __up;
__up.src = 3;
UINT_TO_HOST(__up.src, 8);
在宏扩展之后,它获得了句子:__up.src = __up.src;
,它只能给出一个垃圾值,因为在宏的块范围内再次定义了对象__up
。
这里最近讨论了这种麻烦(第1部分的第一部分,然后跳到第5部分):
How much is it possible to create fake-functions with macros in C?
问题是内部变量__up
隐藏了外部变量__up
的值,并且宏不能按预期工作。
因此,它是不可重用的代码。