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;
-
3There's no question about it, a malloc'ed block of memory is definitely a single "array object" as far as pointer arithmetic and §6.5.6 are concerned. (I can't cite chapter and verse, though.)Steve Summit– Steve Summit2019年09月06日 21:43:58 +00:00Commented Sep 6, 2019 at 21:43
-
@SteveSummit Thanks Steve, those words calm me down a bit - I've used this kind of pointer arithmetic in a lot of placesJulius– Julius2019年09月06日 22:22:00 +00:00Commented Sep 6, 2019 at 22:22
4 Answers 4
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.
1 Comment
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.
4 Comments
int *ip = malloc(255 * sizeof(int)); be a pointer to an array? (or uint32_t.) If I allocated storage several objects some struct type using malloc would it still be considered an array?int *, the situation is less clear. The standard treats character pointers specially, allowing us to treat the allocated object (region of storage) as an array of bytes even if nothing has been written to it yet. But, for other types, it has specifications about "effective type" that are a nuisance to interpret. For practical purposes, this pointer arithmetic works. For language-lawyer purposes, somebody else could answer, or I would have to study it another time.uint8_t to unsigned char -- the change would be purely semantic. But, if you have to guarantee the strictest standard compliance for some esoteric hypothetical machine, then consider the change. (@EricPostpischil - another great answer...)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.
Comments
According to the standard:
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.
[C17 § 6.5.6]
The behavior is undefined otherwise. The standard is clear. Since the value returned by malloc can be assigned to a pointer to "any type of object" and then used to access "an array of such objects," [C17 § 7.22.3], the above rule applies.
8 Comments
Explore related questions
See similar questions with these tags.