I was reading the following from the C standard:
(6.5.6 Additive operators)
9 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 result is the difference of the subscripts of the two array elements.
Now I wonder what is considered to be an "array object". To be more specific, I am wondering whether the following silly example is legal? Is that allocated memory block considered one "array object"?
uint8_t *data = malloc(255);
uint8_t *end = data + 255;
ptrdiff_t size = end - data;
I couldn't find anything in the standard adequately defining exactly what constitues an 'array object', but looking at the memory allocation functions in 7.22.3 of the C11 standard draft, I did find this:
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).
It's not as explicit as one might want, but it does indicate that the memory returned from these functions can be used as an array, therefore the rules of pointer arithmetic should apply.
For non-language-lawyer purposes, yes.
For language-lawyer purposes, I do not see that arithmetic is guaranteed with uint8_t
, but it is with a character type (char
, unsigned char
, or signed char
).
Per C 2018 7.22.3.4 2 and 3, if malloc
does not return a null pointer then, the returned value points to allocated space for an object of the requested size. Per 3.15 1, an object is “region of data storage in the execution environment, the contents of which can represent values”. The space provided by malloc
is a region of data storage in the execution environment, and its contents can represent values, even if they do not yet.
If we assigned the result of malloc
to a pointer to a character type, 6.3.2.3 7 would apply: “… When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object.” Although not explicitly stated, this is understood to mean the object may be treated as an array of character type, and this is required by other parts of the C standard, such as 6.5 6 (“If a value is copied into an object having no declared type … as an array of character type,…).
Thus, the pointer arithmetic operations are defined for pointers of type char *
, unsigned char *
, or signed char *
on this object.
While uint8_t
, if it is defined by <stdint.h>
, must have largely the same properties as unsigned char
(both are pure binary, uint8_t
cannot be larger than unsigned char
since unsigned char
must support the value 255, and uint8_t
cannot be smaller than unsigned char
since the character types are by definition the fundamental units of object size), it is not necessary the same type. It could be an extended integer type, as permitted in 6.2.5 4, and thus might not be covered by the rules about converting pointers to character types.
I think the relevant quote from the standard is from Section 7.22.3, "Memory management functions", paragraph 1:
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).
(emphasis mine.)
So the memory returned by malloc
is an array, and calculating a pointer difference as you are is legal.
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