【问题标题】:Consecutive arrays连续数组
【发布时间】:2020-08-26 09:34:14
【问题描述】:

我很好奇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;
}

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

【问题讨论】:

  • 我不认为在这里指定数组排除了它是单个对象的可能性。单个 int 也可以视为大小为 1 的数组!
  • 该标准还包括以下潜台词:“两个对象可以在内存中相邻,因为它们是较大数组的相邻元素或结构的相邻成员,它们之间没有填充,或者因为实现选择即使它们不相关,也要如此放置它们。”

标签: arrays c


【解决方案1】:

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

它没有。 “... start of a different array ...” 是一种简化。下一个规范是:

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


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

是的。

【讨论】:

  • @Lundin 是的,但取消引用不是 OP 问题的一部分,这个答案也不建议这样做。
【解决方案2】:

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

编辑:阅读this answer 以获取标准中在引用指针时明确说明这一点的引文)

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

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

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

在您的示例中,如果我将数组更改为 char,则指针可能会比较不相等,因为编译器会选择将 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;
}

【讨论】:

    【解决方案3】:

    标准中的关键词是“或”。

    当且仅当两个指针都是空指针时,两个指针比较相等 是指向同一对象的指针(包括指向对象的指针和 开头的子对象)或函数,两者都是指向一个的指针 在同一数组对象的最后一个元素之后,一个是指向 一个超过一个数组对象的末尾,另一个是指向 恰好紧随其后的不同数组对象的开始 地址空间中的第一个数组对象。

    如果地址空间中有一个数组后跟一个结构,编译器会布局该数组,然后在字边界处对齐结构的开头,而不是立即在数组后面。在这种情况下,不能保证指针相等。在大多数架构中,它们并不相同。

    之所以提到数组作为约束是因为数组可以包含任何内容:std::byte、char、int、long、double 等,指针解引用操作是否成功取决于正在执行的操作指向并访问。如果您将指针指向内存中的特定字节并将其取消引用为 long,如果它未在字边界上对齐,则会出现分段错误。如果您将其取消引用为 char 或 std::byte,它将成功。指针不关心这一点,但取消引用它的调用者必须关心。请记住,在取消引用之前,指针可以转换为不同的类型。

    这就是标准特别包含数组的原因。

    正如其他人所指出的,标准中指针相等条件的第一部分有助于实现迭代器、数组访问、iterator::begin()、iterator::end() 等,

    【讨论】:

    • Re “std::byte”:这个问题被标记为 C,而不是 C++。
    猜你喜欢
    • 2015-01-15
    • 1970-01-01
    • 2021-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-09
    • 2021-03-25
    相关资源
    最近更新 更多