News for hackers. Stuff that matters.
Re:Why Does Calloc Exist? by wizeman on Monday November 14, 2022 @10:33AM (#33595452)
I think you may be right and I was wrong.

I think I was operating under more conservative assumptions about what is actually legal to do in C.

I actually had to go and read the standard because the strict aliasing rules can be very tricky and it's not always clear what is legal to do in every situation. Even then, it's hard to understand the wording of the standard and its implications, especially since there are multiple rules coming into play and there's no clear explanation of how they interact with each other.

In this specific case that we're talking about, I think you can indeed allocate a buffer, store the malloc-returned pointer in a `char *` variable, and then store an element and a size_t wherever you want within the buffer (but if you want to store them or access them using a properly-typed pointer rather than memcpy() then the address needs to respect the alignment of the type, I think).

But I think this is only legal because the addresses within a buffer returned by malloc are not considered to have a declared type and that the type of some address within the buffer becomes established whenever you assign or copy some value to it, so it doesn't actually necessarily become established based on the valid conversions between pointer types like I thought it was.

I'm still not exactly 100% certain about what the expression we're talking about is actually undefined behavior or not, because on one hand, the standard clearly says that dereferencing a pointer which points one past the end of an array is undefined behavior (and `p` could be considered such a pointer), but on the other hand, it's hard to see how this can be the case when `p` can be a perfectly valid pointer to some other value within the same allocation.

The rule which says that it's undefined behavior to dereference a pointer that points one past the end of an array (which can be a single value) is inside the pointer arithmetic section. So my interpretation is that, if there is a valid and properly-typed value stored at the address of a pointer that also points to one past the end of an array, it's only undefined behavior to dereference such a pointer if the address of that pointer was obtained through pointer arithmetic based on a pointer that also pointed to the array.

So, if my interpretation is correct, I think in this case the rules are subtle: it seems that dereferencing `((size_t *) p)[-1]` is legal if there already is a `size_t` stored at that address (I think?). But it doesn't seem to be legal to dereference `(((size_t *) p)[-1])[1]` (unless `p` points to some non-first element of an array). However, it would be legal to dereference `*((size_t *) p)`, but only if the address represented by `p` already contains a `size_t` there, I think (??).

There are no comments on this comment yet.