3

为什么会这样:

#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' 作为数组索引会产生非常量表达式。我不明白编译器如何在编译时评估表达式。为什么不将此标记为错误?

4

5 回答 5

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 实现中)结构中的数组元素。

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

于 2013-10-21T23:12:59.337 回答
1

这是一个编译时构造

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

[C99, 7.17]:

宏...

offsetof(type, member-designator)

...

类型和成员代号应使给定的

static type t;

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

于 2013-10-21T21:44:39.527 回答
0

偏移量(类型,成员) 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 标准)

于 2013-10-21T21:45:42.637 回答
0

我想我现在明白了。

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

于 2013-10-22T14:40:59.930 回答
0

究竟什么是允许作为成员指定者存在一些混淆。以下是我知道的两篇论文:

然而,即使是相当老的 GCC、clang 和 ICC 版本也支持使用动态偏移量计算数组元素。根据Raymond 的博客,我猜 MSVC 也早就支持它了。


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

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;
}

诚然,后半部分只是一个理论。这是一个非常有用的优化,我想最初它是故意允许用于这种情况的,或者它被意外支持,然后发现对这种情况很有用。

于 2020-10-19T01:00:04.980 回答