Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is difference between two pointers pointing to the same deallocated array well-defined in C?

#include <stddef.h>
#include <stdlib.h>

int main(void)
{
  const size_t length = 10000;
  double* a = malloc(length * sizeof *a);
  double* first = a;
  double* last = a + length-1;
  free(a);
  ptrdiff_t diff = last - first;
}

In C, is it well-defined to take the difference of two pointers pointing to some locations in the same array after it has been deallocated?

The reason I am asking this is because I have an array of pointers pointing to the same buffer. The buffer gets reallocated, and I need to update the pointers. I do this by computing the difference between pointer n and pointer 0 in the pointer array after the buffer is reallocated. (Of course I can compute the differences before reallocation and store it, but I am a bit curious.)

like image 744
Rasmus Avatar asked Nov 16 '25 08:11

Rasmus


1 Answers

In C, is it well-defined to take the difference of two pointers pointing to some locations on the same array after it has been deallocated?

The answer is no for the C language. Here are the references from the C Standard:

6.2.4 Storage durations of objects
1 An object has a storage duration that determines its lifetime. There are four storage durations: static, thread, automatic, and allocated. Allocated storage is described in 7.24.4.
2 The lifetime of an object is the portion of program execution during which storage is guaranteed to be reserved for it. An object exists, has a constant address, and retains its last-stored value throughout its lifetime. If an object is referred to outside of its lifetime, the behavior is undefined. If a pointer value is used in an evaluation after the object the pointer points to (or just past) reaches the end of its lifetime, the behavior is undefined. The representation of a pointer object becomes indeterminate when the object the pointer points to (or just past) reaches the end of its lifetime.[...]

7.24.4 Memory management functions
[...] The lifetime of an allocated object extends from the allocation until the deallocation

3.25
1 indeterminate representation object representation that either represents an unspecified value or is a non-value representation

The Standard is clear: If a pointer value is used in an evaluation after the object the pointer points to (or just past) reaches the end of its lifetime, the behavior is undefined. So you cannot use the pointers to compute the offset into the deallocated array.

This may seem extravagant as pointer values should not magically change when the block they point into is freed and pointer arithmetic should still work and produce the expected values. In most cases, it will and you are unlikely to experience this kind of undefined behavior on regular environments, but the Standard hints that The representation of a pointer object becomes indeterminate when the object the pointer points to (or just past) reaches the end of its lifetime. This means that merely using the pointer value can have undefined behavior, eg: because they could be trap values.

Here are a few examples of environments where this could pose problems:

  • running under supervision of debugging tools such as Valgrind
  • running in an interpreter such as ch
  • running in segment protected mode where merely loading an invalid pointer value into segment or address registers triggers an exception (eg: Windows x86 16-bit segmented architecture used in the '80s and '90s)
  • running on an OS that uses signed pointers. Currently the signatures are only checked upon dereferencing, but the C Standard allows for exception to be triggered on merely loading invalid pointers (in address registers).

So the answer is no and here are possible solutions:

  • Your workaround of computing the old size before realloc, while the pointers are still valid.
  • Do not use realloc to reallocate the block; use malloc to allocate a new block and copy the contents explicitly (you should know the required length) and update the other pointers before freeing the old block.
  • Use index values into the dynamically allocated block instead of pointers. This is also less risky and does not require relocation but does need some extra code to access the data. If your arrays can't be larger than 2³² doubles (32 GiB), this allows uint32_t indices for half the cache footprint of pointers or size_t indices.
like image 136
chqrlie Avatar answered Nov 18 '25 22:11

chqrlie



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!