是否合法或越界形成了末尾第一个元素的地址? [英] Legal or out-of-bounds forming the address of the first element past the end?

查看:213
本文介绍了是否合法或越界形成了末尾第一个元素的地址?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在另一个问题中,我看到了&studmark[STUDNO][0],其中STUDNO是数组的大小.

In another question I saw &studmark[STUDNO][0] where STUDNO is the size of the array.

我现在想知道该代码是否已经是未定义的行为. studmark[STUDNO]是最后一种,尽管可能会创建它,但不能对其进行访问.用[0]编制索引以形成地址有效吗?还是必须只使用studmark[STUDNO],然后降级为指针的最后一个指针?

I wonder now if that code is already undefined behaviour. studmark[STUDNO] is one-past-the-end and while it may be created it must not be accessed. Is indexing that with [0] to then form the address valid? Or must one use simply studmark[STUDNO] which then degrades to a pointer one-past-the-end?

请以任何一种方式引用该标准.

Arguments either way please with references to the standard.

更新:示例代码和输出

#include <stdio.h>

#define STUDNO 16

int studmark[STUDNO][2];

int main() {
  printf("&studmark = %p\n", studmark);
  printf("&studmark[1][0] = %p\n", &studmark[1][0]);
  printf("&studmark[STUDNO-1][0] = %p\n", &studmark[STUDNO-1][0]);
  printf("&studmark[STUDNO][0] = %p\n", &studmark[STUDNO][0]);
  return 0;
}

编译没有警告和输出:

./foo 
&studmark = 0x601060
&studmark[1][0] = 0x601068
&studmark[STUDNO-1][0] = 0x6010d8
&studmark[STUDNO][0] = 0x6010e0

推荐答案

鉴于studmark的定义如下:

int studmark[STUDNO][2];

然后表达式&studmark[STUDNO][0]调用未定义的行为.

为使指针取消引用更加明显,首先,我们将从数组索引表示形式切换为指针表示形式. C11标准的6.5.2.1p2节规定:

To make the pointer dereferences more apparent, first we'll switch from array index notation from pointer notation. Section 6.5.2.1p2 of the C11 standard states:

下标运算符[]的定义是E1[E2]是 与(*((E1)+(E2)))

The definition of the subscript operator [] is that E1[E2] is identical to (*((E1)+(E2)))

因此上面的表达式变为:

So the above expression becomes:

&*(studmark[STUDNO] + 0)

哪个会成为:

&*(*(studmark + STUDNO) + 0)

此表达式以&*运算符开头.当&优先于*时,它们彼此抵消.这在6.5.3.2p3节中有详细说明:

This expression starts with the & and * operators. When & preceedes * they cancel each other out. This spelled out in section 6.5.3.2p3:

一元&运算符产生其操作数的地址.如果 操作数的类型为类型",结果的类型为类型指针". 如果 操作数是一元*运算符的结果,该运算符都不是 也不会评估&运算符,并且结果就像 省略,只不过对操作员的约束仍然适用,并且 结果不是左值.

The unary & operator yields the address of its operand. If the operand has type "type", the result has type "pointer to type". 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.

因此可以简化为:

*(studmark + STUDNO) + 0

现在,我们来看一下添加项.这是有效的,因为根据6.5.6p8节,创建指向数组末尾一个元素的指针是合法的:

Now we look at the addition. This is valid because creating a pointer to one element past the end of the array is legal as per section 6.5.6p8:

将具有整数类型的表达式添加或减去时 从指针开始,结果具有指针操作数的类型.如果 指针操作数指向数组对象的元素,并且该数组 足够大,结果指向的元素与 原始元素,使得下标的差异 结果数组元素和原始数组元素等于整数表达式. 换句话说,如果表达式P指向 i th 数组对象的元素,表达式(P)+N(等效为 N+(P))和(P)-N(其中N的值为 n )指向, i + n th i−n th 元素 数组对象的大小(如果存在).而且,如果表达 P指向数组对象的最后一个元素,即表达式 (P)+1指向数组对象的最后一个元素,如果 表达式Q指向数组的最后一个元素 对象,表达式(Q)-1指向数组的最后一个元素 目的. 如果指针操作数和结果都指向元素 相同的数组对象,或者在数组的最后一个元素之后 目标,评估不得产生溢出;否则, 行为未定义.如果结果指向最后一个元素之后的一个 数组对象的值,不得将其用作一元对象的操作数 *被评估的运算符.

When an expression that has integer type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the pointer operand points to an element of an array object, and the array is large enough, the result points to an element offset from the original element such that the difference of the subscripts of the resulting and original array elements equals the integer expression. In other words, if the expression P points to the ith element of an array object, the expressions (P)+N (equivalently, N+(P)) and (P)-N (where N has the value n) point to, respectively, the i+nth and i−nth elements of the array object, provided they exist. Moreover, if the expression P points to the last element of an array object, the expression (P)+1 points one past the last element of the array object, and if the expression Q points one past the last element of an array object, the expression (Q)-1 points to the last element of the array object. If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined. If the result points one past the last element of the array object, it shall not be used as the operand of a unary * operator that is evaluated.

这意味着studmark + STUDNO是有效的指针,但不能取消引用.这就是问题所在.*(studmark + STUDNO)调用未定义的行为,因为它取消了对数组末尾的一个元素的引用.

This means studmark + STUDNO is a valid pointer, but it cannot be dereferenced. This is where the problem comes in. *(studmark + STUDNO) invokes undefined behavior because it dereferences one element past the end of the array.

所以&studmark[STUDNO][0]是未定义的行为.

相反,这是有效的:

&studmark[STUDNO]

等于:

&*(studmark + STUDNO)

随后:

studmark + STUDNO

因为它创建了指向数组末尾一个元素的指针,但没有取消引用它.

Because it creates a pointer to one element past the end of an array but does not dereference it.

这篇关于是否合法或越界形成了末尾第一个元素的地址?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆