Specifically, is the following code, the line below the marker, OK?
struct S{
int a;
};
#include <stdlib.h>
int main(){
struct S *p;
p = malloc(sizeof(struct S) + 1000);
// This line:
*(&(p->a) + 1) = 0;
}
People have argued here, but no one has given a convincing explanation or reference.
Their arguments are on a slightly different base, yet essentially the same
typedef struct _pack{
int64_t c;
} pack;
int main(){
pack *p;
char str[9] = "aaaaaaaa"; // Input
size_t len = offsetof(pack, c) + (strlen(str) + 1);
p = malloc(len);
// This line, with similar intention:
strcpy((char*)&(p->c), str);
// ^^^^^^^
The intent at least since the standardization of C in 1989 has been that implementations are allowed to check array bounds for array accesses.
The member
p->a
is an object of typeint
. C11 6.5.6p7 says thatThus
is a pointer to an
int
; but it is also as if it were a pointer to the first element of an array of length 1, withint
as the object type.Now 6.5.6p8 allows one to calculate
&(p->a) + 1
which is a pointer to just past the end of the array, so there is no undefined behaviour. However, the dereference of such a pointer is invalid. From Appendix J.2 where it is spelt out, the behaviour is undefined when:In the expression above, there is only one array, the one (as if) with exactly 1 element. If
&(p->a) + 1
is dereferenced, the array with length 1 is accessed out of bounds and undefined behaviour occurs, i.e.With the note saying that:
That the most common behaviour is ignoring the situation completely, i.e. behaving as if the pointer referenced the memory location just after, doesn't mean that other kind of behaviour wouldn't be acceptable from the standard's point of view - the standard allows every imaginable and unimaginable outcome.
There has been claims that the C11 standard text has been written vaguely, and the intention of the committee should be that this indeed be allowed, and previously it would have been alright. It is not true. Read the part from the committee response to [Defect Report #017 dated 10 Dec 1992 to C89].
There is no reason why the same wouldn't be transferred to scalar members of structures, especially when 6.5.6p7 says that a pointer to them should be considered to behave 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.
If you want to address the consecutive
struct
s, you can always take the pointer to the first member and cast that as the pointer to thestruct
and advance that instead:This is undefined behavior, as you are accessing something that is not an array (
int a
withinstruct S
) as an array, and out of bounds at that.The correct way to achieve what you want, is to use an array without a size as the last
struct
member:C standard guarantees that
§6.7.2.1/15:
&(p->a)
is equivalent to(int *)p
.&(p->a) + 1
will be address of the element of the second struct. In this case, only one element is there, there will not be any padding in the structure so this will work but where there will be padding this code will break and leads to undefined behaviour.