Path: csiph.com!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail From: Tim Rentsch Newsgroups: comp.lang.c++ Subject: Re: C++20 concepts rocks Date: Sun, 06 Feb 2022 11:20:03 -0800 Organization: A noiseless patient Spider Lines: 97 Message-ID: <86fsovkdjw.fsf@linuxsc.com> References: <87k0easj5d.fsf@bsb.me.uk> <865yptlpgf.fsf@linuxsc.com> <87mtj4qd0h.fsf@bsb.me.uk> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Injection-Info: reader02.eternal-september.org; posting-host="a80c60510ddf69c3dc6a890da3a37a42"; logging-data="27264"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1/hUaoHbZHMVw0Jxcn8N26CKQKzeWgxm/w=" User-Agent: Gnus/5.11 (Gnus v5.11) Emacs/22.4 (gnu/linux) Cancel-Lock: sha1:sN4RyDPyE3N7a7HsM1dW+PyfnH0= sha1:gsxqmF3XUMh9bQtobHWeKVAqpAw= Xref: csiph.com comp.lang.c++:82942 Ben Bacarisse writes: > "Alf P. Steinbach" writes: > >> The code using `(&result)[1]` involves a couple of extra conversions > > Eh? char *ep = r + sizeof(r); involves one array-to-pointer conversion > just as char *ep = (&r)[1]; does. > >> and indirections and is thus needlessly complex, plus it's formally >> UB, > > Can you point to where this is made UB in the C++ standard? > > There is an ambiguous phrase in the C standard ("undefined behaviour if > the * is evaluated") but that phrase is entirely missing in the C++ > text. And I got lost in the forest of rvalues, lvalues, prvalues, > xvalues and glvalues without finding anything undefined. Can you find > it? [...] Let me take a stab at explaining. There is undefined behavior in the code that Alf originally responded to, but where the UB is and why it is UB has not been explained very well. Two points to start: there is nothing wrong with the original initializing declaration; and the aspect of dereferencing is a red herring. To begin suppose we have this code: char foo[2][20]; char *p = foo[1]; There is nothing wrong with this code. After these declarations the variable p points at the first element of foo[1]; But a problem happens if we want to use p to create pointer values that point to elements of foo[0], as for example char *p_prime = p-1; The rules for pointer arithmetic don't allow this. The reason is p points an element of foo[1], but p-1 doesn't. In both C and C++ the semantic description for adding an integer to a pointer defines the result only if P and P+N are both elements of the same array (or one past the last element). But that isn't true here. The variable p unambiguously points at an element of foo[1], but p-1 does not. If we have two pointers p and q char foo[2][20]; char *p = foo[1]; char *q = foo[0] + 20; the pointers p and q will (and must) compare equal, but they are not interchangeable, because they point into different subarrays of foo. Going back to the earlier code, we have char *ep = (&result)[1]; There is nothing wrong with this initialization. But by virtue of taking the address of result, which is an array, we have in effect created a two-dimensional array, and have initialized ep with a pointer that points into the "second subarray" of that two-dimensional array. The created pointer value is not allowed to participate in arithmetic that would take it into the first subarray. So later in the earlier code when we use this expression ep - 4 the code is asking for a pointer to an object in (&result)[1]. But there is no such object. The expressions ep - 0 and ep + 0 would both be okay, but only because what is being added or subtracted is zero; if any non-zero value were used there is undefined behavior, because the resulting pointer value does not point to an object in the same subarray. (In effect there is one and only one object in the second subarray, but trying to access that mythical object also falls into the realm of undefined behavior.) References: section 6.5.6 paragraph 8 for C (n1570); section 7.6.6 paragraph 4.2 for C++ (N4860). For the record, IMO neither the C standard nor the C++ standard describes plainly enough what the rules are for multi-dimensional arrays. I understand why the rules are the way they are, but I would like to see one or both standards state how things work in a more lucid fashion. Does this help clear things up?