In C, it is perfectly well to make a pointer that points to one past the last element of an array and use it in pointer arithmetics, as long as you don't dereference it:
int a[5], *p = a+5, diff = p-a; // Well-defined
However, these are UBs:
p = a+6; int b = *(a+5), diff = p-a; // Dereferencing and pointer arithmetic
Now I have a question: Does this apply to dynamically allocated memory? Assume I'm only using a pointer pointing to one-past-the-last in pointer arithmetics, without dereferencing it, and malloc()
succeeds.
int *a = malloc(5 * sizeof(*a)); assert(a != NULL, "Memory allocation failed"); // Question: int *p = a+5; int diff = p-a; // Use in pointer arithmetic?
C malloc() method The “malloc” or “memory allocation” method in C is used to dynamically allocate a single large block of memory with the specified size. It returns a pointer of type void which can be cast into a pointer of any form.
First, when a program executes malloc(n) , there is no name. It is not possible to refer to the object by name because there is no name that refers to the space that malloc allocated. Consider int *p = malloc(n); . This assigns the pointer that malloc returns to p .
The user could enter any value he wanted for steve, which means the arr could be any size at all. Since the compiler needs to know how much space to tell the computer to set aside, this code won't work. So, how do we get around this? The answer is dynamic memory allocation, and for that, we need pointers.
Dynamically allocated memory must be referred to by pointers. the computer memory which can be accessed by the identifier (the name of the variable). integer, 8, stored. The other is the “value” or address of the memory location.
The draft n4296 for C11 is explicit that pointing one past an array is perfecly defined: 6.5.6 Language / Expressions / Additive operators:
§ 8 When an expression that has integer type is added to or subtracted from a pointer, the result has the type of the pointer operand. ... Moreover, if the expression P 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 expression Q points one past the last element of an array object, the expression (Q)-1 points to the last element of the array object... 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.
As the type of the memory is never precised in the sub clause, it applies to any type of memory including allocated one.
That clearly means that after:
int *a = malloc(5 * sizeof(*a)); assert(a != NULL, "Memory allocation failed");
both
int *p = a+5; int diff = p-a;
are perfectly defined and as the usual pointer arithmetic rules apply, diff
shall receive the value 5
.
Is it well-defined to use a pointer pointing to one-past-malloc?
It is well defined if p
is pointing to one past the allocated memory and it is not dereferenced.
n1570 - §6.5.6 (p8):
[...] 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.
Subtracting two pointers are valid only when they point to elements of the same array object or one past the last element of the array object, otherwise it will result in undefined behavior.
(p9):
When two pointers are subtracted, both shall point to elements of the same array object, or one past the last element of the array object [...]
The above quotes are well applicable for both dynamically and statically allocated memory.
int a[5]; ptrdiff_t diff = &a[5] - &a[0]; // Well-defined int *d = malloc(5 * sizeof(*d)); assert(d != NULL, "Memory allocation failed"); diff = &d[5] - &d[0]; // Well-defined
Another reason that this is valid for dynamically allocated memory, as pointed by Jonathan Leffler in a comment is:
§7.22.3 (p1):
The order and contiguity of storage allocated by successive calls to the
aligned_alloc
,calloc
,malloc
, andrealloc
functions is unspecified. The pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object with a fundamental alignment requirement and then used to access such an object or an array of such objects in the space allocated (until the space is explicitly deallocated).
The pointer returned by malloc
in the above snippet is assigned to d
and the memory allocated is an array of 5 int
objects.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With