Path: csiph.com!eternal-september.org!feeder.eternal-september.org!reader01.eternal-september.org!.POSTED!not-for-mail
From: Tim Rentsch
Newsgroups: comp.lang.c
Subject: Re: Indexing one past the end of array?
Date: Sun, 06 Oct 2019 08:05:38 -0700
Organization: A noiseless patient Spider
Lines: 83
Message-ID: <867e5hap8d.fsf@linuxsc.com>
References: <874l0moqit.fsf@bsb.me.uk> <5g9mF.47865$hL1.2883@fx35.iad>
Mime-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Injection-Info: reader02.eternal-september.org; posting-host="a0bfc71282e15a9952a47d73886d6801"; logging-data="15253"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1/Dn6o0ti0KDjQxiB+lkgC+BBmCtNU7Hbk="
User-Agent: Gnus/5.11 (Gnus v5.11) Emacs/22.4 (gnu/linux)
Cancel-Lock: sha1:tDdAiDaxGqmXjoV1nCPFgk/phKw= sha1:uMGl+od2z1B4dP+I4dG+0vR8ZXM=
Xref: csiph.com comp.lang.c:146644
Richard Damon writes:
> On 10/5/19 5:01 PM, Ben Bacarisse wrote:
>
>> Keith Thompson writes:
>>
>>> Johann 'Myrkraverk' Oskarsson writes:
>>>
>>>> Dear comp.lang.c,
>>>>
>>>> I've been reading a thread on LWN[1], notably this quote:
>>>>
>>>>> Just some pedantry, but I believe you're allowed to point to the first
>>>>> element after the end of an array, as long as you don't dereference
>>>>> that element. Pointer comparisons with such a pointer are supposed to
>>>>> work, too.
>>>>
>>>> Elsewhere, I've read that on embedded systems memory can be allocated
>>>> right up to the end of memory, and therefore such indexes are invalid
>>>> (but I cannot remember where).
>>>>
>>>> So, is it always legal to calculate one past the end of an array or
>>>> allocated memory?
>>>>
>>>> [1] https://lwn.net/Articles/278137/
>>>
>>> Yes, it's always legal. The language guarantees it.
>>
>>
>>
>>> A compiler needs to do whatever it has to to make this work as
>>> specified. Allocating an object at the very end of memory doesn't
>>> necessarily break this, as long as pointer addition and subtraction
>>> yield the expected results.
>>
>>
>>
>>> For example, assume a system with 32-bit addresses and exactly 2**32
>>> bytes of addressable memory. Assume an array object
>>> char buf[100];
>>> is allocated starting at address 2**32-100. Then (buf+100) points just
>>> past the end of the array, and also just past the end of addressable
>>> memory. As long as evaluating (buf+100)-1 yields a valid pointer to
>>> buf[99], that's ok.
>>
>> A nit: the relational operators (<, >, <=, >=) have to work on the
>> resulting pointers as well. For example (buf+100)-1 < (buf+100) must be
>> true, and that's not automatically the case where pointers are
>> interpreted as unsigned bit patterns. If, however, pointers are signed
>> 2's complement values, it will all come out in the wash.
>>
>>> (This is just an example. The standard doesn't
>>> assume this kind of mapping between addresses and integer values.)
>>
>
> If pointers act like signed values, then the problem occurs at the
> midpoint (so sooner).
If pointers are taken as being signed, presumably the address space
starts at the most pointer value. The whole space is still usable.
> Pointer differences are generally considered signed, and if pointer
> values are compared by this signed difference, then the issue only
> happens if a given array is over half the address space (which might
> well not be well supported).
The type of subtracting one pointer from another has been a signed
type (ptrdiff_t) since C90. As you point out in the next paragraph
there is a problem only when the number of elements being spanned
is greater than PTRDIFF_MAX or less than PTRDIFF_MIN (or the
analogous values in C90). This range may be larger than half the
address space, but is very likely not in most implementations.
However, when size_t is 16 bits, the whole address space will be
usable, as PTRDIFF_MAX must be at least 65535 (C99 onwards).
> If ptrdiff_t is the same size and size_t, than you get issues with
> arrays with more than SIZE_T_MAX/2 elements giving you overflow, but it
> is rare enough that I don't think many implementations make ptrdiff_t
> bigger than size_t to handle it.
The Standard gives an out here, by saying any difference of
pointers that gives a value not representable in type ptrdiff_t
gives undefined behavior.