offsetof在msvc上的实现细节解释?

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的实现, 我没有看懂它, 有以下几个问题(有些伸手党的嫌疑, 不过还是厚着脸皮贴出来):

  1. 这里的两处引用的意义是什么?
  2. 此处除了reinterpret_cast, 还能不能用其它cast? (额, 有点伸手党, 请原谅)
  3. 这里为什么要用volatile?
  4. 不像ANSI C, 这里最后没用- (char*) 0的操作, 那么是如何计算出偏移量呢?
  5. 可不可以用std::ptrdiff_t实现? 可以的话为什么编译器没有这么做?
  6. 既然还是用的0, 而不是nullptr, 那么是通过什么方式解决开头所说的对null pointer进行解引用的问题?
  7. 既然用了reinterpret_cast, 说明已经用c++11重写了, 那么为什么不用nullptr, 而用的是0?

UPDATE

我在知乎上提问后vczh解释
不过现在问题被关闭了, 搞不得知乎的审核员是怎么想的, 这个问题很像科班学生的作业吗?

UPDATE2

我爆栈网上提问了, https://stackoverflow.com/que...
关注此问题的朋友可以去那里看下.

阅读 4.2k
2 个回答

也不是很懂。壮着胆子回答一下:

  1. 第一个&是取地址,第二个&才是引用。总的效果是取0->m的地址。C++是文明进步的,C里面用指针的地方都尽量用引用代替了。

  2. 不知道。但reinterpret_cast不会产生任何机器指令,因此不会导致段错误。

  3. 不知道呢,同问。

  4. (char*) 0还是0呀,减不减都没差了。

  5. 谁和谁取diff呢?参考4.

  6. 参考2.

  7. nullptr并不能和0划等号,它只是一个符号常量。比如某编译器可以把nullptr编译成数字7,只要它保证其他正常的地址都不会等于7,那也应该是允许的。但是我们这里需要的是0,而不是一个由编译器决定的值。

补充一下。reinterpret_cast并不是C++11的,C++98就有了。
另外C++原本规定的空指针就是0。而C语言的(void *) 0。C语言中void *和普通指针是可以隐式转换的,而C++是不允许的。所以NULL的定义不一样。
而且NULL甚至不是标准里的东西,而是一个惯用的实现。所以以前C++更习惯用0表示空指针。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题