连续数组

Pep*_*Pep 16 c arrays

我很好奇C18标准中的一句话:

两个指针比较相等当且仅当它们都是空指针,都是指向同一个对象的指针(包括指向一个对象的指针和它开头的子对象)或函数,都是指向同一数组最后一个元素之后的指针对象,或者一个是指向一个数组对象末尾的指针,另一个是指向另一个数组对象开头的指针,该对象恰好紧跟地址空间中的第一个数组对象。§ 6.5.9 6

为什么数组后面的对象必须是另一个数组?不能简单地成为与数组基类型相同类型的对象(例如int紧跟在 之后的int[])?

难怪我试过这个代码:

#include <stdio.h>

struct test { int arr[10]; int i; };

int main() {
    struct test t;
    int *p, *q;
    p = t.arr + 10;
    q = &t.i;
    if (p == q)
        printf("Equal pointers.");
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

它产生相等的指针。这种行为是否根本没有保证,只是实现定义的巧合?

chu*_*ica 24

OP:为什么数组后面的对象必须是另一个数组?

它不是。“...开始不同的数组...”是一种简化。下一个规范是:

对于这些运算符而言,指向不是数组元素的对象的指针的行为与指向长度为 1 的数组的第一个元素的指针的行为相同,该数组的元素类型为对象的类型。C17dr § 6.5.9 7


OP:不能简单地是一个类型与数组基本类型相同的对象(比如int紧跟在 之后的int[])?

是的。

  • @Lundin是的,但取消引用不是OP问题的一部分,这个答案也不表明可以。 (6认同)

th3*_*3lf 8

首先,在此处指定数组并不排除/禁止单个对象。内存中的单个对象与大小为 1 的数组无法区分。

编辑:阅读此答案以获取标准中的引文,该标准在提及指针时明确说明了这一点)

其次,该标准还试图澄清您引用的声明,以下脚注指出了该规则适用的场景:

两个对象可以在内存中相邻,因为它们是更大数组的相邻元素或结构的相邻成员,它们之间没有填充,或者因为实现选择这样放置它们,即使它们不相关。

综上所述,标准在这里试图说明的是,一般而言,指向不同对象的两个指针不应该比较相等。然而,由于在内存中指向一个数组对象之外的对象是合法的,如果在那个位置碰巧有一个不同的(数组)对象,这样的指针与指向相邻对象的指针进行比较仍然是合法的。现在,由于对齐选择和填充,此位置可能有也可能没有有效对象,但如果有,则这些指针比较相等是可以接受的。

在您的示例中,如果我将数组更改为字符,则指针可能比较不相等,因为编译器会选择将 int 对齐到 4 个字节(在大多数 32 或 64 位平台上),从而引入填充。根据标准,这种行为仍然是合法的。

#include <stdio.h>

struct test { char arr[10]; int i; };

int main() {
    struct test t;
    int *p, *q;
    p = (int*)(t.arr + 10);
    q = &t.i;
    if(p == q)
      printf("Equal pointers.");
    else
      printf("Unequal pointers.");
    return 0;
}
Run Code Online (Sandbox Code Playgroud)