C语言 为什么非常量偏移表达式有效



为什么这样做:

#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("%dn", function(atoi(argv[1])));
}

如果我没记错 offsetof 的定义,它是一个编译时构造。使用"i"作为数组索引会导致非常量表达式。 我不明白编译器如何在编译时计算表达式。为什么这没有标记为错误?

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 实现中)。

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

这是一个编译时构造

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

[C99, 7.17]:

宏...

offsetof(type, member-designator)

类型和成员指示符应为给定

static type t;

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

offsetof (type,member) 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 标准)

我想我现在明白了。

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

对于究竟

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

  • DR 496
  • 指向成员的指针的偏移量

但是,即使是相当旧版本的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;
}

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

相关内容

  • 没有找到相关文章

最新更新