【问题标题】:Is comparing to a pointer one element past the end of an array well-defined?与指针相比,数组末尾的一个元素是否定义明确?
【发布时间】:2018-01-02 06:48:33
【问题描述】:

我从this question 了解到,递增 NULL 指针或递增超过数组末尾不是明确定义的行为:

int* pointer = 0;
pointer++;

int a[3];
int* pointer2 = &a[0];
pointer2 += 4; 

但是如果指向一个无效位置的指针只用于比较,而他所在位置的内存永远不会被访问呢?

例子:

void exampleFunction(int arrayLen, char** strArray)
{
    for(char** str = strArray; str < strArray + arrayLen; str++) //or even str < &strArray[arrayLen]
    {
        //here *str is always a pointer to the first char of my string
    }
}

在这里,我将指针与数组末尾后一个元素的指针进行比较。这是明确定义的行为吗?

【问题讨论】:

  • *argv + argc 这个表达式什么时候有意义? argv[0] 的长度与argc 无关。如果您的意思是argv + argc,那么 AIUI 很好,因为指针“数组末尾之后的一个元素”是明确定义的,并且可以与同一数组中的其他指针进行比较。 (或者argv + argc + 1,因为正如chux 所说,argvNULL 结尾。)
  • @chux 很高兴知道!然后我只需要使用&lt;=。但是最初的问题不应该考虑我的错误。更新了更好的例子
  • 您的第二个代码没问题,因为str 始终是一个有效的指针。不过,它的最后一个值是不可取消引用的。
  • 编辑后的表达式*strArray + arrayLen 仍然没有意义。
  • 在数组末尾增加 一步​​ 就可以了。进一步增加不是。 pointer2 += 4 是未定义的行为,但 pointer2 += 3 不会是,即使它会导致 pointer2 指向数组的末尾。

标签: c arrays pointers undefined-behavior


【解决方案1】:

比较指针在数组末尾的一步是明确定义的。但是,您的 pointerpointer2 示例是未定义的,即使您实际上对这些指针什么都不做。

指针可能指向数组末尾之后的一个元素。该指针可能不会被取消引用(否则将是undefined behavior),但它可以与数组中的另一个指针进行比较。

C standard 的第 6.5.6 节对指针添加(添加了重点)进行了以下说明:

8 如果指针操作数和结果都指向同一个数组对象的元素,或超过数组最后一个元素 对象,评估不会产生溢出; 否则, 行为未定义。如果结果指向最后一个元素 数组对象,不得用作一元*的操作数 被评估的运算符。

第 6.5.8 节对指针比较进行了如下说明(已添加重点):

5 当比较两个指针时,结果取决于所指向的对象在地址空间中的相对位置。 如果两个 指向对象类型的指针都指向同一个对象,或者都指向 超过同一数组对象的最后一个元素,它们比较 相等。 如果指向的对象是同一个聚合的成员 对象,指向稍后声明的结构成员的指针比较更大 而不是指向结构中较早声明的成员的指针,并且 指向具有较大下标值的数组元素的指针比较 大于指向同一数组元素的指针 下标值。指向同一联合对象成员的所有指针 比较相等。 如果表达式 P 指向数组的一个元素 对象和表达式 Q 指向同一对象的最后一个元素 数组对象,指针表达式 Q+1 比较大于 P。在 在所有其他情况下,行为未定义。

对于pointer1,它开始指向NULL。增加这个指针会调用未定义的行为,因为它没有指向一个有效的对象。

对于pointer2,它增加了 4,将其 两个 元素放在数组末尾之后,而不是一个,所以这又是未定义的行为。如果将其增加 3,则行为将得到很好的定义。

【讨论】:

  • “一次通过”规则不也适用于非数组对象吗? int x; int *p = &amp;x+1;
  • @chux 你可能是对的。第 6.5.8 节第 4 部分说 “出于这些运算符的目的,指向不是数组元素的对象的指针与指向长度为 one 的数组的第一个元素的指针的行为相同,类型为对象作为其元素类型。”第 6.5.6 部分第 7 部分具有相同的措辞。
  • 引用的 6.5.8 适用于 &lt;&lt;=&gt;=&gt;==!= 运算符在给定指向对象的指针、“刚刚过去”对象的指针或空指针时永远不会产生未定义的行为。 “指向”一个对象的指针和“刚刚过去”另一个对象的指针之间的比较可能会报告相等,但这并不意味着指针可以互换使用。
  • 这是否也适用于数组之前的元素?我问是因为我需要反向迭代,最好的习惯用法是在第一个元素之前一步的指针。
  • @LeviMorrison 它没有
猜你喜欢
  • 2020-04-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-13
  • 1970-01-01
相关资源
最近更新 更多