Yes I can understand that having defined an array with the name array as:
int array[10][5];
with array[7][6] we are accessing out of the array (and the
implementation sequence).
Out of the 7th array, yes; but not out of the implementation sequence:
7*5+6 = 41 < 10*5 = 50
Yes, you are right, it is out of bounds for the array since it is
defined as int array[10][5]; but my uses are not out of the bounds of
the declared pointer types.
Also consider this. We can initialise the array in both ways:
int array[10][5]=
{{1,1,1,1,1},{1,1,1,1,1},{1,1,1,1,1},{1,1,1,1,1},{1,1,1,1,1},{1,1,1,1,1},{1,1,1,1,1},{1,1,1,1,1},{1,1,1,1,1},{1,1,1,1,1}};
and
int array[10][5]=
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
(as a sequence).
You _assume_ that 50 int that lie contiguously in memory can be treated as
an array and that pointer-arithmetic can be used to access any of them
given a pointer to the first. That hypothesis, however, is not waranted by
the standard. To get rid of all the function call ramifications in your
example, consider the following:
int array [10][5] = {0};
int* p = &array[0][0]; // line 2
std::cout << p[5] << '\n'; // line 3
This compares to your code since &array[0][0] is what array[0] decays to
when passed as a parameter to some_func.
The question is whether the third line has undefined behavior. Consider the
following hypothetical implementation of pointers: a pointer is a tripple
of three addresses, the first to the pointee and the other two specifying a
valid range for pointer arithmetic. Similarly, every array (static or
dynamic) has its bounds stored somewhere. When an array decays to a
pointer, these bounds are used to deduce the range for the pointer.
Whenever pointer-arithmetic yields a pointer outside the valid range,
dereferencing triggers a segfault. Such an implementation is not ruled out
by any provisions of the standard that I know of.
Note that in line 2, the compiler has static type information about the rhs.
The rhs is a pointer to the first element in an array of 5. Thus, in the
described implementation, p will be given a range of size 5 and line 3 is
an out-of-bounds access since it dereferences the past-end position of the
5 int sequence array[0] in a way that is obtained through pointer
arithmetic from a pointer into the 5 int sequence array[0].
If you think that a range-checking implementation is not standard
conforming, please provide some language from the standard supporting your
point of view. Note that the guarantee of arrays being contiguous is met by
such an implementation. What such an implementation prevents is just the
reinterpretation of array sizes through pointer casting and pointer
arithmetic.
OK, I agree that it is not explicitly guaranteed by the standard, but I
think in reality it always works. But as it was said, it is not
guaranteed by the standard.
Case closed.
I asked a similar question regarding C90 code in clc, since with very
few exceptions, C90 is a subset of C++03 and here are the question and
the answers I got:
Question:
Are the following guaranteed to work always as *C90* code?
1.
#include <stdio.h>
void some_func(int *p, const size_t SIZE)
{
size_t i;
for(i=0; i<SIZE; ++i)
printf("%d ", p
);
}
int main(void)
{
int array[10][5]= {0};
some_func(array[0], sizeof(array)/sizeof(**array));
puts("");
return 0;
}
The above prints 50 zeros. I think it is guaranteed to work, since all
arrays are sequences of their elements.
2.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
size_t i, j;
int array[50]= {0};
int (*p)[5]= (int (*)[5])(&array[0]);
for (i= 0; i< 10; ++i)
for(j=0; j<5; ++j)
printf("%d ", p[j]);
puts("");
return 0;
}
Here p behaves as a 2-dimensional matrix, that is a 10x5 matrix. I think
it is guaranteed to work for the same reason as the first one, that is
we can treat an array (sequence) of integers as various types of integer
arrays.
=========================================================================
Answer 1:
It is not guaranteed to work.
The problem is that there is no array of int
with more than 5 members declared anywhere.
Pointers to char are allowed to step through the bytes
of any object, but that's by a special rule.
I can't conceive of any mechanism by which your code could fail,
but the guarantee of which you speak, is not there.
Your pointer to int,
is over running the boundaries of an array of 5 int.
-- pete
=========================================================================
Answer 2:
It's not necessarily legal, but it ought to be. There is a related
example at the end of 6.7.5.3 in the C99 Rationale (non-normative of
course),
----------------------------------------------------------------------
The following example demonstrates how to declare parameters
in any order and avoid lexical ordering issues
void g(double *ap, int n)
{
double (*a)[n]= (double (*)[n]) ap;
/* ... */ a[1][2] /* ... */
}
in this case, the parameter ap is assigned to a local
pointer that is declared to be a pointer to a variable
length array. The function g can be called as in
{
double x[10][10];
g(&x[0][0], 10);
}
----------------------------------------------------------------------
This sort of thing is common in numerical software and (some) authors
of the standard intended it to work, but the normative standard doesn't
necessarily guarantee it. Personally I would consider any failing
implementation as broken.
-- pa at panix dot com