代码之家  ›  专栏  ›  技术社区  ›  Zan Lynx

取一个过去的地址数组元素通过下标:合法的C++标准或不?

  •  70
  • Zan Lynx  · 技术社区  · 15 年前

    我已经看到它多次声明,C++代码不允许使用以下代码:

    int array[5];
    int *array_begin = &array[0];
    int *array_end = &array[5];
    

    &array[5] 法律C++代码在这个上下文中?

    如果可能的话,我想要一个参考标准的答案。

    了解它是否符合C标准也很有趣。如果它不是标准C++,为什么要做出不同的处理 array + 5 &array[4] + 1 ?

    13 回复  |  直到 9 年前
        1
  •  45
  •   Adam Rosenfield    15 年前

    是的,这是合法的。从 C99 draft standard :

    §6.5.2.1第2段:

    后缀表达式后跟方括号中的表达式 [] 指定数组对象的元素。下标运算符的定义 [] 是吗 E1[E2] 与相同 (*((E1)+(E2))) 应用于二进制文件 + 接线员,如果 E1 是数组对象(相当于指向 数组对象的初始元素)和 E2 是一个整数, 指定 E2 -th 元素 E1 (从零开始计数)。

    §6.5.3.2第3段(重点):

    一元 & 运算符生成其操作数的地址。如果操作数的类型为 类型 , 类型 . 如果操作数是一元数的结果 * 操作人员 & 对运算符进行求值,结果就好像两个运算符都进行了求值一样 省略,但运算符上的约束仍然适用,并且结果不是 如果操作数是 [] 接线员,以及;运算符或一元数 * 这一点由 [] 进行评估,结果就像 & 操作人员 [] 操作员已更改为a + 操作人员 . 否则,结果是 指向由其操作数指定的对象或函数的指针。

    §6.5.6,第8段:

    在指针中添加或减去整数类型的表达式时 结果具有指针操作数的类型。如果指针操作数指向 数组对象,并且数组足够大,结果指向从 原始元素,使得结果和原始元素的下标不同 数组元素等于整数表达式。换句话说,如果表达式 P 这个 i (P)+N (相当于, N+(P) )及 (P)-N (在哪里 N 有价值 n i+n -th和 i−n -元素 数组对象,前提是它们存在。此外,如果表达式 指向最后一个 数组对象的元素,表达式 (P)+1 点的最后一个元素 数组对象,如果表达式 Q 指向数组对象的最后一个元素, 表情 (Q)-1 指向数组对象的最后一个元素。如果两个指针 操作数和结果指向同一数组对象的元素,或超过最后一个的元素 数组对象的元素,求值时不应产生溢出;否则 行为是未定义的。如果结果指向数组对象的最后一个元素后一个元素,则 不得用作一元数的操作数 * 要计算的运算符。

    请注意,该标准明确允许指针指向数组末尾的一个元素, 前提是它们没有被取消引用 . 根据6.5.2.1和6.5.3.2,表达式 &array[5] &*(array + 5) ,相当于 (array+5) ,它指向数组末尾的另一端。这不会导致取消引用(按6.5.3.2),因此是合法的。

        2
  •  41
  •   John Kugelman Michael Hodel    10 年前

    您的示例是合法的,但这只是因为您没有实际使用越界指针。

    让我们先来处理越界指针(因为我最初是这样解释您的问题的,在我注意到示例使用了一个超过结束指针的指针之前):

    一般来说,你甚至不被允许这样做 . 没有别的地方。

    指针甚至不允许存在,这意味着您显然也不允许取消引用它。

    以下是标准对该主题的说明:

    5.7:5:

    当一个表达式具有整数 指针,结果的类型为 指针操作数。如果指针 操作数指向 数组对象,并且数组很大 元素的差异 结果和的下标 积分表达式。换句话说,, 如果表达式P指向第i个 数组对象的元素 N+(P))和(P)-N(其中N具有 值n)分别指向 数组对象,前提是它们存在。 此外,如果表达式P指向 到数组的最后一个元素 对象,如果表达式Q指向 一个数组的最后一个元素 数组对象的最后一个元素。 如果指针操作数和 结果指向相同的元素 数组对象,或超过最后一个 数组对象的元素 评估不应产生任何结果 覆盖; 否则,该行为是错误的 未定义

    (强调矿山)

    当然,这是针对操作员+。因此,可以肯定的是,以下是标准对阵列订阅的说明:

    5.2.1:1:

    表情 E1[E2] 与(否认)相同 *((E1)+(E2))

    [注意:例如,超过数组(5.7)末尾的地址将被视为 指向可能位于该地址的arrays元素类型的不相关对象。[完注]

    感谢ilproxyil在这里更正了最后一点,回答了您问题的最后一部分:

    • array + 5 取消引用任何内容,它只是 创建一个指针,指向超过端点的指针 array .
    • &array[4] + 1 array+4 (这是非常安全的), 获取该左值的地址,并且 向该地址添加一个 结果超出结束指针1 (但那个指针永远不会被击中
    • &array[5] 解引用数组+5 (据我所知,这是合法的, “数组元素类型”的 上面说的),然后采取 该元素的地址,该元素也 看起来很合法。

    所以他们做的事情并不完全相同,尽管在这种情况下,最终结果是一样的。

        3
  •  17
  •   Tyler McHenry    15 年前

    信息技术 合法的

    According to the gcc documentation for C++ , &array[5] 这是合法的。在C++中 and in C 您可以安全地将元素寻址到数组末尾之后的一个位置-您将获得一个有效指针。所以 & 因为表达是合法的。

    但是,即使指针指向有效地址,尝试取消对未分配内存的指针的引用仍然是未定义的行为。因此,即使指针本身是有效的,尝试取消引用该表达式生成的指针仍然是未定义的行为(即非法)。

    但实际上,我认为这通常不会导致撞车。

    编辑:顺便说一句,STL容器的end()迭代器通常是这样实现的(作为指向末尾的指针),这很好地证明了这种做法是合法的。

    编辑:哦,现在我明白了,你并不是在问持有指向该地址的指针是否合法,而是问获取指针的确切方式是否合法。我会听从其他人的回答。

        4
  •  10
  •   Richard Corden    15 年前

    232 具有以下特征:

    我们一致认为标准中的方法似乎是可行的:p=0*P这并非天生的错误。左值到右值的转换将使其具有未定义的行为

    虽然这是一个稍有不同的示例,但它确实显示了“*”不会导致左值到右值的转换,因此,假设表达式是“&”的直接操作数它期望一个左值,然后定义行为。

        5
  •  9
  •   CB Bailey    15 年前

    我不认为这是违法的,但我确实相信&数组[5]未定义。

    • 5.2.1[expr.sub]E1[E2]与*((E1)+(E2))相同(根据定义)

    • 5.3.1[expr.unary.op]一元*运算符。。。结果是一个左值,它引用表达式指向的对象或函数。

    • 1.3.12[定义未定义]当本国际标准省略任何明确的行为定义时,也可能出现未定义的行为。

    正如其他地方指出的那样, array + 5 &array[0] + 5 是有效且定义良好的方法,用于获取超出数组末尾的指针。

        6
  •  7
  •   Todd Gardner    15 年前

    除了以上的答案,我还将指出操作员&可以为类重写。因此,即使它对pod有效,对您知道无效的对象也可能不是一个好主意(就像首先覆盖操作符&())。

        7
  •  4
  •   user207421    10 年前

    这是合法的:

    int array[5];
    int *array_begin = &array[0];
    int *array_end = &array[5];
    

    第5.2.1节将表达式E1[E2]下标为*((E1)+(E2))

    因此,我们可以说array_end也是等价的:

    int *array_end = &(*((array) + 5)); // or &(*(array + 5))
    

    指向函数类型和类型的指针 表达式所指向的。 如果表达式的类型是指向T的指针,则结果的类型是T。[注意:指向不完整类型(其他类型)的指针 可以取消对cv(无效)的引用。由此获得的左值可以以有限的方式使用(例如初始化引用) 榜样);此左值不得转换为右值,请参见4.1。-结束注释]

    上述内容的重要部分:

    '结果是引用对象或函数的左值'。

    一元运算符“*”返回一个引用int的左值(无反引用)。一元运算符'&'然后获取左值的地址。

    只要没有对越界指针的反引用,那么该操作就完全由标准覆盖,并且定义了所有行为。因此,通过我的阅读,上述内容是完全合法的。

    许多STL算法依赖于定义良好的行为,这一事实暗示了标准委员会已经意识到了这一点,我确信有一种东西明确地涵盖了这一点。

    下面的评论部分提出了两个论点:

    (请阅读:但它很长,我们最终都变成了巨魔)

    论据1

    由于第5.7节第5段的规定,这是非法的

    将具有整数类型的表达式添加到指针或从指针中减去时,结果具有指针操作数的类型。如果指针操作数指向数组对象的某个元素,且数组足够大,则结果将指向与原始元素偏移的元素,以便生成的数组元素与原始数组元素的下标之差等于整数表达式。换句话说,如果表达式P指向数组对象的第i个元素,则表达式(P)+N(相当于N+(P))和(P)-N(其中N的值为N)分别指向数组对象的第i+N个和第i N个元素,前提是它们存在。此外,如果表达式P指向数组对象的最后一个元素,表达式(P)+1指向数组对象的最后一个元素,如果表达式Q指向数组对象的最后一个元素,则表达式(Q)-1指向数组对象的最后一个元素。如果指针操作数和结果都指向同一数组对象的元素,或一个过去

    尽管这一部分是相关的;它不显示未定义的行为。我们正在讨论的数组中的所有元素要么在数组中,要么在数组的末尾(这在上面的段落中有很好的定义)。

    论据2:

    下面提出的第二个论点是: *
    虽然这是用来描述“*”运算符的常用术语;在标准中故意避免使用该术语,因为术语“去引用”在语言和对底层硬件的含义方面没有得到很好的定义。

    尽管访问超出数组末尾的内存显然是未定义的行为。我不相信 unary * operator 访问内存(读取/写入内存) 关于这点 (不是以标准定义的方式)。在这种情况下(根据标准的定义(见5.3.1.1)) 一元*运算符 返回一个 lvalue referring to the object . 在我对语言的理解中,这不是对底层记忆的访问。然后,该表达式的结果立即由 unary & operator 运算符,该运算符返回 指对象的左值

    许多其他参考维基百科和非规范来源。所有这些我都觉得无关紧要。 C++是由标准定义的 .

    结论:

    下文提供了这些信息。如果你给我看一个标准参考,说明这是UB。我会的

    1. 留下答案。
    2. 加上所有的大写字母,这是愚蠢的,我错了,让所有人都读。

    这不是一个论点:

    并不是所有的东西都是由C++标准定义的。敞开心扉。

        8
  •  2
  •   Matthew Flaschen    15 年前

    n2798 ):

    “一元运算符的结果是 指向其操作数的指针。操作数 应为左值或限定id。 在第一种情况下,如果 表达式是–T–的类型 结果是指向T的指针”(第103页)

    数组[5]不是我所能说的合格id(列表在第87页);最接近的似乎是标识符,但虽然数组是标识符,但数组[5]不是。它不是左值,因为“左值指的是对象或函数。”(第76页)。数组[5]显然不是函数,也不能保证引用有效对象(因为数组+5位于最后分配的数组元素之后)。

    显然,它可能在某些情况下工作,但它不是有效的C++或安全的。

    注意:通过数组添加一个是合法的(第113页):

    指向数组的最后一个元素 对象,表达式(P)+1点 对象,如果表达式Q指向 一个数组的最后一个元素 对象,表达式(Q)-1指向 数组对象的最后一个元素。 结果指向相同的元素 数组对象,或超过最后一个 数组对象的元素 评估不应产生任何结果 overow“

    但使用&这样做是不合法的;。

        9
  •  2
  •   rlbond    14 年前

    编辑:如果你想让它对称,你可以写

    int* array_begin = array; 
    int* array_end = array + 5;
    
        10
  •  1
  •   JohnB    11 年前

    它应该是未定义的行为,原因如下:

    1. 尝试访问越界元素会导致未定义的行为。因此,该标准并不禁止在这种情况下抛出异常的实现(即,在访问元素之前检查边界的实现)。如果 & (array[size]) 被定义为 begin (array) + size ,在越界访问时引发异常的实现将不再符合标准。

    2. 这是不可能的 end (array) if数组不是数组而是任意集合类型。

        11
  •  0
  •   David Thornley    15 年前

    地址常量表达式是指向左值的指针……指针应使用一元和;运算符…或使用数组(4.2)类型的表达式。下标运算符[]…可用于创建地址常量表达式,但不能使用这些运算符访问对象的值。如果使用下标运算符,其操作数之一应为整型常量表达式。

    在我看来像&数组(5)是合法C++,是地址常量表达式。

        12
  •  -1
  •   Aditya Sehgal    15 年前

    如果您的示例不是一般情况,而是特定情况,那么它是允许的。你可以 合法地 但它不适用于一般情况,即尝试访问距离数组末端1远的元素。

    link text

        13
  •  -2
  •   codymanix    15 年前

    这是完全合法的。