C标准寻址简化不一致

Chr*_*utz 11 c standards simplification memory-address

第§6.5.3.2节"地址和间接操作符"3表示(仅限相关部分):

一元&运算符返回其操作数的地址....如果操作数是一元运算*符的结果,则不会对该运算符和&运算符进行求值,结果就好像两者都被省略,除了对运算符的约束仍然适用且结果不是左值.类似地,如果操作数是[]运算符的结果,&则不会对运算符或*由其暗示的一元[]进行求值,结果就像&删除了[]运算符并将运算符更改为+运算符一样....

这意味着:

#define NUM 10
int tmp[NUM];
int *i = tmp;
printf("%ti\n", (ptrdiff_t) (&*i - i) );
printf("%ti\n", (ptrdiff_t) (&i[NUM] - i) );
Run Code Online (Sandbox Code Playgroud)

应该完全合法,打印0和NUM(10).标准似乎非常清楚,这两种情况都需要进行优化.

但是,它似乎不需要优化以下内容:

struct { int a; short b; } tmp, *s = tmp;
printf("%ti\n", (ptrdiff_t) (&s->b - s) );
Run Code Online (Sandbox Code Playgroud)

这似乎非常不一致.我没有理由认为上面的代码不应该打印sizeof(int)加号(不太可能)填充(可能是4).

简化&->表达式在概念上(IMHO)将是&[]一个简单的地址加偏移量.它甚至是一个在编译时可以确定的偏移量,而不是[]运算符可能的运行时间.

关于为什么这么看似不一致的理由是否存在?

Jam*_*lis 4

在你的例子中,&i[10]实际上是不合法的:它变成了i + 10,它变成了NULL + 10,并且你不能对空指针执行算术。(6.5.6/8列出了可以进行指针运算的条件)

无论如何,这条规则是在C99中添加的;它在 C89 中不存在。我的理解是,它的添加很大程度上是为了使代码像下面这样定义良好:

int* begin, * end;
int v[10];

begin = &v[0];
end = &v[10];
Run Code Online (Sandbox Code Playgroud)

最后一行在 C89(和 C++)中技术上是无效的,但由于这条规则而在 C99 中是允许的。这是一个相对较小的变化,使常用的结构有了明确的定义。

因为您无法对空指针执行算术,所以您的示例 ( &s->b) 无论如何都是无效的。

至于为什么会出现这种“不一致”,我只能猜测。很可能没有人想到要使其保持一致,或者没有人看到一个令人信服的用例。这可能是经过考虑并最终被拒绝的。没有关于&*减少理由的评论。您也许可以在WG14 论文中找到一些明确的信息,但不幸的是,它们的组织似乎相当糟糕,因此搜索它们可能会很乏味。