为什么非常量offsetof表达式有效?

时间:2013-10-21 21:40:19

标签: c offsetof

为什么这样做:

#include <sys/types.h>
#include <stdio.h>
#include <stddef.h>

typedef struct x {
    int a;
    int b[128];
} x_t;


int function(int i)
{
  size_t a;

  a = offsetof(x_t, b[i]);

  return a;
}

int main(int argc, char **argv)
{
    printf("%d\n", function(atoi(argv[1])));
}

如果我没记错了offsetof的定义,那就是编译时构造。使用'i'作为数组索引会导致非常量表达式。我不明白编译器如何在编译时评估表达式。 为什么这不会被标记为错误?

5 个答案:

答案 0 :(得分:2)

C标准不要求这样做,但它可能适用于某些C实现,因为offsetof(type, member)扩展为:

type t; // Declare an object of type "type".
char *start = (char *) &t; // Find starting address of object.
char *p = (char *) &t->member; // Find address of member.
p - start; // Evaluate offset from start to member.

我已将上述内容分成几部分以显示基本逻辑。 offsetof的实际实现会有所不同,可能使用依赖于实现的功能,但核心思想是从对象内成员的地址中减去虚构或临时对象的地址,这样的结果在抵消。它旨在为成员工作,但作为一种意想不到的效果,它也适用于(在某些C实现中)结构中数组元素。

它适用于这些元素只是因为用于查找成员地址的构造也可用于查找数组成员元素的地址,并且指针的减法以自然的方式工作。

答案 1 :(得分:1)

  

这是一个编译时构造

AFAICS,没有这样的限制。所有标准都说是:

  

[C99,7.17]:

     

宏......

offsetof(type, member-designator)
     

...

     

类型和成员指示符应为

static type t;
     

然后表达式&(t.member-designator)计算为地址常量。

答案 2 :(得分:0)

offsetof(类型,成员) Return member offset: This macro with functional form returns the offset value in bytes of member member in the data structure or union type type.

http://www.cplusplus.com/reference/cstddef/offsetof/ (C,C ++ 98和C ++ 11标准)

答案 3 :(得分:0)

我想我现在明白这一点。

offsetof()宏不计算为常量,它计算为返回偏移量的运行时表达式。因此,只要type.member是有效语法,编译器就不关心它是什么。您可以对数组索引使用任意表达式。我以为它就像sizeof,并且必须在编译时保持不变。

答案 4 :(得分:0)

对于确切指定为会员代号的人有些困惑。这是我知道的两篇论文:

但是,即使是相当老版本的GCC,clang和ICC也支持计算具有动态偏移量的数组元素。基于Raymond's blog,我想MSVC早就支持它了。


我认为这是出于实用主义。对于那些不熟悉的人,“结构黑客”和灵活的数组成员在结构的最后一个成员中使用可变长度数据:

struct string {
  size_t size;
  const char data[];
};

此类型通常分配有以下内容:

string *string_alloc(size_t size) {
  string *s = malloc(offsetof(string, data[size]));
  s->size = size;
  return s;
}

诚然,后一部分只是一个理论。这是一个非常有用的优化,我以为最初是针对此类情况而故意允许的,或者偶然得到了支持,然后发现它确实对此类情况有用。