为什么在C和C++中,offsetof实现的差异如此奇怪?

5

我打开了 stddef.h,看到了这个:

#if defined _MSC_VER && !defined _CRT_USE_BUILTIN_OFFSETOF
    #ifdef __cplusplus
        #define offsetof(s,m) ((size_t)&reinterpret_cast<char const volatile&>((((s*)0)->m)))
    #else
        #define offsetof(s,m) ((size_t)&(((s*)0)->m))
    #endif
#else
    #define offsetof(s,m) __builtin_offsetof(s,m)
#endif

在C++编译器中,__cplusplus分支的实现非常奇怪,我认为它是多余的。 在C编译器的else分支中,字段偏移量的计算更简单。 我已经测试过它,它能够正常工作。 那么第一种情况下使用这些奇怪的转换和类型限定符有什么用呢?


@RemyLebeau 我认为我不同意你的说法。C++强制转换比C强制转换更安全。但是:C风格的强制转换确实具有编译时检查:例如,您不能在不相关的类之间进行强制转换。虽然C++强制转换具有更多的编译时检查,但它仍然允许您执行无效的操作:例如,在int和float之间reinterpret_cast:int a = 24; float f = *reinterpret_cast<float*>(&a); 这个代码可以编译而没有任何错误。 - bolov
@bolov,尽管如此,这仍然是指针之间的转换。但是,“reinterpret_cast”比C风格的转换更安全,因为后者将执行“static_cast”,“reinterpret_cast”和“const_cast”中的任何一个或甚至是前者与后者的某种组合(例如,“static_cast”加上“const_cast”)。 - Arne Vogel
顺便说一下,如果用户代码包含空指针解引用,那么它将是未定义的行为。然而,这是在平台头文件中,平台开发人员拥有特殊权限... - Arne Vogel
1
@ArneVogel 是的,我说过C++的类型转换比C语言的类型转换更安全。 - bolov
1个回答

12

operator & 可以被重载为类型 m,因此 &((s*)0)->m) 会调用该 operator &,而不是取 m 的地址。

const volatile 存在于 reinterpret_cast 的类型中,所以它可以在 mconst 和/或 volatile 时正常工作。

请注意,在 C++11 中,有 std::addressof(x) 函数,它总是获取 x 的地址,无论是否重载了 operator &。它的实现方式很可能与问题中所见的类似。


4
注意,C++11及以后版本有std::addressof()来处理operator&的问题。offsetof()可以写成 #define offsetof(s,m) ((size_t)std::addressof(((s*)0)->m)) - Remy Lebeau
@RemyLebeau 很好的观点。我一直在考虑,但最终没有提到。现在提了出来。 - Angew is no longer proud of SO

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接