问题
In another question I saw &studmark[STUDNO][0]
where STUDNO
is the size of the array.
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.
Update: Sample code and output
#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;
}
Compiling gives no warnings and outputs:
./foo
&studmark = 0x601060
&studmark[1][0] = 0x601068
&studmark[STUDNO-1][0] = 0x6010d8
&studmark[STUDNO][0] = 0x6010e0
回答1:
Given that the definition of studmark
looks like this:
int studmark[STUDNO][2];
Then the expression &studmark[STUDNO][0]
invokes undefined behavior.
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:
The definition of the subscript operator
[]
is thatE1[E2]
is identical to(*((E1)+(E2)))
So the above expression becomes:
&*(studmark[STUDNO] + 0)
Which becomes:
&*(*(studmark + STUDNO) + 0)
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.
So this can be reduced to:
*(studmark + STUDNO) + 0
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:
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
(whereN
has the value n) point to, respectively, the i+nth and i−nth elements of the array object, provided they exist. Moreover, if the expressionP
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 expressionQ
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.
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.
So &studmark[STUDNO][0]
is undefined behavior.
In contrast, this is valid:
&studmark[STUDNO]
As it is equal to:
&*(studmark + STUDNO)
And subsequently:
studmark + STUDNO
Because it creates a pointer to one element past the end of an array but does not dereference it.
来源:https://stackoverflow.com/questions/58858493/legal-or-out-of-bounds-forming-the-address-of-the-first-element-past-the-end