Sharath said:
I had an argument with someone on wheather this piece of code can
invoke undefined bahaviour.
I think it does not invoke any undefined behaviour since there is
sufficient memory space of 9 integer elements starting from the in the
address passed, but the other person insisted that it would invoke
undefined behaviour(for whatever reasons he had).
void fill(int *p)
{
for(unsigned j=0;j<9;++j)
p[j]=0;
}
int main()
{
int arr[3][3];
fill(&arr[0][0]);
}
So please let me know if this code invokes undefined behaviour and why?
Sharath A.V
I think this code might throw an exception (or something like that) on
a bound checked implementation.
The relevant C++ standard paragraph is 5.7-5
[expr.add]
"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."
First, p[4] is equivalent to *(p+4). The type of the pointer is int*.
The type of the element pointed-to by the pointer is int
It is clear, that:
"If both the pointer operand and the result point to elements of the
same array object."
The pointer points to an *element* of an array object. The pointer
points to int, and thus the array object is of type int[3].
The array is not an int[3][3], otherwise it would not make sense... an
int* pointing to an int[3]...So the array object to which it refers is
the first int[3] object of this array of 3 int[3].
And, p+1 is ok, p+2 is ok, p+3 is ok (pointer one past the last
element), but p+4 has undefined behavior.
If the implementation is bound checked (and the WG21 deliberately tried
to allow such implementation) it might throw a runtime exception.
A bound checked implementation would simply use pointers containing,
not only the raw memory address, but also information about the bounds
of the array directly this element.
Similarly, even if you have an implementation where the ABI says that:
struct X {
int x;
int y;
int z;
} s;
x, y and z are contiguous into memory.
It doesn't mean that you'll be able to do (&s.x)[2] to access s.z
Indeed, the standard says (5.7-4 [expr.add])
"
4 For the purposes of these operators, a pointer to a nonarray
object behaves the same as a pointer to the first element of an
array of length one with the type of the object as its element type."
Well, here it is very clear that &s.x is a pointer to a nonarray
object, and thus, a bound checked implementation will use, as bound
info, that it lives in an array of size 1.
So, (&s.x)+1 is still ok... But with a bound checked implementation,
(&s.x)+1 != s.y (the bound checked implementation will see that they
don't live in the "same array"). And, (&s.x)+2 has UB.
Well, I think that the problem is similar with int[3][3].
With a bound checked implementation, &arr[3] should be different from
&arr[1][0]
For example, if the underlying implementation of pointers is:
struct {raw_pointer start, ptr, end;};
start and end will be different for &arr[3] than for &arr[1][0]
In a sense, they both live in different memory spaces.