C 标准寻址简化不一致

标签 c standards memory-address simplification

第 6.5.3.2 节“地址和间接运算符”¶3 说(仅相关部分):

The unary & operator returns the address of its operand. ... If the operand is the result of a unary * operator, neither that operator nor the & operator is evaluated and the result is as if both were omitted, except that the constraints on the operators still apply and the result is not an lvalue. Similarly, if the operand is the result of a [] operator, neither the & operator nor the unary * that is implied by the [] is evaluated and the result is as if the & operator were removed and the [] operator were changed to a + operator. ...

这意味着:

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

应该是完全合法的,打印 0 和 NUM (10)。标准似乎非常明确,这两种情况都需要优化。

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

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

这看起来非常不一致。我看不出上面的代码没有理由不打印 sizeof(int) 加上(不太可能)填充(可能是 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];

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

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

至于为什么会出现这种“不一致”,我只能猜测。很可能没有人想过让它保持一致,或者没有人看到一个令人信服的用例。这可能会被考虑并最终被拒绝。 the Rationale 中没有关于 &* 减少的评论.您或许可以在 the WG14 papers 中找到一些权威信息。 , 但不幸的是,它们的组织似乎很差,因此拖网搜索它们可能很乏味。

关于C 标准寻址简化不一致,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4905512/

相关文章:

c - 如何创建一个 (C) 函数来使用 'read' 将文件中的数据读取到链接列表中?

c - Linux 服务器 C 代码在收到信号后停止

c - 我可以使用哪种现代 C 编译器来构建这个 1992 MS-DOS 程序?

xcode - 如何使用atos手动符号化崩溃日志

c++ - 无法使用 eclipse cdt 在 C/C++ 中打开文件

database - 打开标准 Jet DB 4.0 文件 (*.vmd)

c++ - 该标准是否指定哪些 header 包含其他 header ?

c++ - 新数组放置需要缓冲区中未指定的开销?

c++ - 寻址函数时数组下标的无效类型

C 指针和地址