使用stddef.h的offsetof而不是滚动自己的可移植性

时间:2011-07-14 21:24:34

标签: c standards portability

这是一个挑剔的细节问题,有三个部分。上下文是我希望说服一些人无条件地使用<stddef.h> offsetof的定义而不是(在某些情况下)滚动自己的定义是安全的。有问题的程序完全用普通的旧C编写,所以在回答时请完全忽略C ++

第1部分:当以与标准offsetof相同的方式使用时,此宏的扩展是否会引发每个C89的未定义行为,为什么或为什么不行,并且它在C99中是不同的?

#define offset_of(tp, member) (((char*) &((tp*)0)->member) - (char*)0)

注意:人们感兴趣的所有实现都取代了标准的规则,指针只有在指向同一个数组时才能相互减去,通过定义所有指针,无论类型或值,指向进入一个全球地址空间。因此,当依赖于该规则时,我认为此宏的扩展会引发未定义的行为。

第2部分:据你所知,有没有一个已发布的生产C实现,当用上面的宏扩展时,(在某些情况下)会表现出与它{{{已经使用了1}}宏?

第3部分:据您所知,最近发布的生产C实现是什么,它没有提供offsetof,或者没有在该标题中提供stddef.h的工作定义?该实现是否声称符合任何版本的C标准?

对于第2部分和第3部分,请仅在您可以命名特定实施并给出发布日期时回答。说明可能符合条件的实现的一般特征的答案对我没用。

4 个答案:

答案 0 :(得分:10)

无法编写可移植的offsetof宏。您必须使用stddef.h提供的那个。

关于您的具体问题:

  1. 宏调用未定义的行为。除非它们指向同一个数组,否则不能减去指针。
  2. 实际行为的最大区别在于宏不是整型常量表达式,因此它不能安全地用于静态初始化器,位域宽度等。另外,严格的边界检查 - C类实现可能会完全破坏它。
  3. 从来没有任何C标准缺少stddef.hoffsetof。前ANSI编译器可能缺乏它,但它们有更多的基本问题使它们无法用于现代代码(例如缺少void *const)。
  4. 此外,即使某些理论编译器确实缺少stddef.h,您也可以只提供替换,就像人们放入stdint.h以便与MSVC一起使用...

答案 1 :(得分:6)

答案 2 :(得分:2)

(1)在进行减法之前,未定义的行为已存在。

  1. 首先,(tp*)0不是您认为的那样。它是 null 指针,这样的野兽不一定用全零表示 位模式。
  2. 然后,成员运算符->不仅仅是偏移量加法。在具有分段内存的CPU上,这可能是一个更复杂的操作。
  3. 如果表达式为,则使用&操作的地址为UB 不是有效的对象。
  4. (2)对于第2点,当然还有使用分段存储器的野外(嵌入式东西)。对于3.,R对整数常量表达式的要点有另一个缺点:如果代码被严格优化,&操作可能在运行时完成并发出错误信号。

    (3)从未听说过这样的事情,但这可能不足以让你的同事们得到安慰。

答案 3 :(得分:1)

我相信几乎每个优化编译器都会在多个时间点打破该宏。你的同事显然很幸运,不会被它击中。

一些初级编译工程师会决定,因为零页面永远不会映射到他们选择的平台上,所以当任何人用指向该页面的指针做任何事情时,这是未定义的行为,他们可以安全地优化掉整个表达式。那时候,每个人的自制软件都会破坏,直到有足够多的人对它进行尖叫,我们这些足够聪明的人不会对我们的业务感到高兴。

我不知道这是当前发布版本中的行为的任何编译器,但我想我已经看到它在某些时候发生在我曾经使用的每个编译器上。