MSVC's implemention
#define offsetof(s,m) ((size_t)&reinterpret_cast<char const volatile&>((((s*)0)->m)))
ANSI C' implemention
#define offsetof(st, m) \
((size_t) ( (char *)&((st *)(0))->m - (char *)0 ))
背景
我知道在ANSI C中, &((st *)(0))->m
不会对0(NULL)真正的进行解引用, 而会直接返回m
的地址, 这样就避免了对NULL解引用会造成的段错误. 而在c++中, 并没有这样的规则, 所以如果想ANSI C这样实现offsetof会引起段错误. 也知道char*
是各种type中standard里唯一保证过sizeof是1, 其它都是implemention dependent.
而具体到MSVC的实现, 我没有看懂它, 有以下几个问题(有些伸手党的嫌疑, 不过还是厚着脸皮贴出来):
- 这里的两处引用的意义是什么?
- 此处除了reinterpret_cast, 还能不能用其它cast? (额, 有点伸手党, 请原谅)
- 这里为什么要用volatile?
- 不像ANSI C, 这里最后没用
- (char*) 0
的操作, 那么是如何计算出偏移量呢? - 可不可以用std::ptrdiff_t实现? 可以的话为什么编译器没有这么做?
- 既然还是用的0, 而不是nullptr, 那么是通过什么方式解决开头所说的对null pointer进行解引用的问题?
- 既然用了
reinterpret_cast
, 说明已经用c++11重写了, 那么为什么不用nullptr, 而用的是0?
UPDATE
我在知乎上提问后vczh的解释
不过现在问题被关闭了, 搞不得知乎的审核员是怎么想的, 这个问题很像科班学生的作业吗?
UPDATE2
我爆栈网上提问了, https://stackoverflow.com/que...
关注此问题的朋友可以去那里看下.
也不是很懂。壮着胆子回答一下:
第一个
&
是取地址,第二个&
才是引用。总的效果是取0->m
的地址。C++是文明进步的,C里面用指针的地方都尽量用引用代替了。不知道。但
reinterpret_cast
不会产生任何机器指令,因此不会导致段错误。不知道呢,同问。
(char*) 0
还是0呀,减不减都没差了。谁和谁取diff呢?参考4.
参考2.
nullptr
并不能和0划等号,它只是一个符号常量。比如某编译器可以把nullptr
编译成数字7,只要它保证其他正常的地址都不会等于7,那也应该是允许的。但是我们这里需要的是0,而不是一个由编译器决定的值。