当我读到这个宏时,我有点困惑: #define g_once_init_enter(location),这是在glib库中定义的。
#define g_once_init_enter(location) \
(G_GNUC_EXTENSION({ \
G_STATIC_ASSERT(sizeof *(location) == sizeof(gpointer)); \
(void) (0 ? (gpointer) *(location) : 0); \
(!g_atomic_pointer_get (location) && \
g_once_init_enter (location)); \
}))
这一行有什么影响:(void) (0 ? (gpointer) *(location) : 0);
而最后一行又是g_once_init_enter(location)
,它是一个死循环吗?
谢谢你的回复。
答案 0 :(得分:2)
这是一些非常难看的代码。逐行:
G_GNUC_EXTENSION
显然与__extension__
相同,这是隐藏地毯下方不良代码的肮脏技巧。它告诉gcc编译器将非标准代码视为正确的C. G_STATIC_ASSERT(sizeof *(location) == sizeof(gpointer));
是一个前C11静态断言,如果指向的数据与类型gpointer
的大小不同,它将扩展为某种形式的神秘编译器错误。这是尝试获得一点点类型安全的尝试。(void) (0 ? (gpointer) *(location) : 0);
是一种拙劣的尝试,通过编写一条从未执行但包含强制转换的行来实现类型安全。值得注意的是,C中的几乎任何类型都可以转换为几乎任何其他类型,而不会引起编译器错误,因此这条线路根本没有实现。g_once_init_enter
仅在g_atomic_pointer_get
返回0 / NULL的情况下执行。总的来说,宏尝试在C中实现类型安全,但是不能完全管理。主要是因为它不可能完成任务,至少在C11之前。
值得注意的是,有一个名为gpointer
的类型是有问题的,因为它意味着该类型根本不是指针,或者它是隐藏在typedef后面的指针。在任何一种情况下,都是非常糟糕的做法。
在现代C中你可能会用这个代替这整个混乱:
#define g_once_init_enter(location) \
_Generic(*(location), gpointer : \
!g_atomic_pointer_get (location) && g_once_init_enter (location) )
答案 1 :(得分:1)
这看起来像是对我的隐式类型检查。
宏显然希望传递gpointer
的地址(无论是什么),但是宏没有参数类型,所以它不能这么说。
相反,它断言sizeof *(location)
与sizeof(gpointer)
相同,后者验证location
可以被解除引用,并且它指向的内容具有正确的大小(否则G_STATIC_ASSERT
行不会编译)。
然后通过编译强制转换(location)
,确保gpointer
指向的任何内容都可以转换为(gpointer) *(location)
。这一行没有其他效果(并且在运行时永远不会达到强制转换);如果演员阵容无效,那只会让编译器抱怨。
宏不会递归扩展。最后一行g_once_init_enter(location)
保持原样,因此必须在某处可以调用实际的g_once_init_enter
函数。