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.