Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it well-defined to use a pointer pointing to one-past-malloc?

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? 
like image 323
iBug Avatar asked Dec 20 '17 07:12

iBug


People also ask

Does malloc use pointer?

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.

Why does malloc need a pointer?

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 .

Why do we use pointer in dynamic memory allocation?

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.

Can we dynamically allocate memory to pointer?

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.


2 Answers

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.

like image 76
Serge Ballesta Avatar answered Oct 02 '22 12:10

Serge Ballesta


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, and realloc 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.

like image 20
haccks Avatar answered Oct 02 '22 12:10

haccks