6

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;
Christian Gibbons
4,3801 gold badge19 silver badges32 bronze badges
asked Sep 6, 2019 at 21:26
2
  • 3
    There'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.) Commented 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 places Commented Sep 6, 2019 at 22:22

4 Answers 4

6

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.

answered Sep 6, 2019 at 21:43
Sign up to request clarification or add additional context in comments.

1 Comment

When reading the standard, I cannot really understand if resting objects actually have type (but still can be accessed using lvalue of some specific other types, when the standard allows that), or if the only type they "have" is the type that is is used to access them. It's confusing.
5

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.

answered Sep 6, 2019 at 21:51

4 Comments

Following your logic, would 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?
@1201ProgramAlarm: With 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.
@EricPostpischil Thanks! if I understand your answer correctly it would be more safe to use unsigned char to be sure? Do you personally think it's worth the effort to change uint8_t types to unsigned chars in an existing project?
It all depends on how much portability you need to guarantee. The "For non-language-lawyer purposes, yes." sums it up. On every current system I can think of, there is no need to change from 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...)
2

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.

answered Sep 6, 2019 at 22:25

Comments

0

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.

answered Sep 6, 2019 at 22:31

8 Comments

While a C17 cite is fine, there are no copies currently available in html form. If the text hasn't changed, it's probably a good idea to cite C11 Standard - latest draft (which also provides links to each paragraph in the standard) E.g C11 Standard - 7.22.3 Memory management functions
It's not a good idea to rely on a draft that's 8 years behind the current standard.
Not saying it is the best alternative, but citing something that the Questioner doesn't have (unless you include the text) isn't a great alternative either. In many worlds (before VS2017), anything newer than C89 was touch and go.
The OP is asking about the current version of the language, not a past version that's been superseded 9 times.
That's fine. It was just a suggestion. It wasn't intended as a ding. The alternative is citing the latest draft of the C17 standard in .pdf (which requires a full download and provides no way to provide a jump-cite to the specific paragraph at issue)
|

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.