Path: ...!eternal-september.org!feeder3.eternal-september.org!news.eternal-september.org!.POSTED!not-for-mail From: James Kuyper Newsgroups: comp.lang.c Subject: Re: Arrays Date: Sun, 29 Sep 2024 10:04:00 -0400 Organization: A noiseless patient Spider Lines: 87 Message-ID: References: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Injection-Date: Sun, 29 Sep 2024 16:04:01 +0200 (CEST) Injection-Info: dont-email.me; posting-host="667f5167d48c302b10d5bc2fb9482fd0"; logging-data="1870002"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX19/atOyeTtgZD3URIp4hvYT0SVWzgfTql4=" User-Agent: Mozilla Thunderbird Cancel-Lock: sha1:d6XO4la1eOVtni0nm+FiyAHJlyw= In-Reply-To: Content-Language: en-US Bytes: 5230 On 9/29/24 07:33, Stefan Ram wrote: > Alright, so I've got a triple whammy of questions coming at me. > It's about this program I've been tinkering with. > > #include > int main() > { char const abc[] = "abcdefhijklmnopqrstuvwxyz"; > char const * const p = abc + 3; > char const( * const def )[] =( char const( * const )[] )p; > printf( "%c\n",( *def )[ -3 ]); } > > 1st > > First off, I'm getting this warning that's throwing me for a loop: > > main.c: In function 'main': > main.c:7:32: warning: cast discards 'const' qualifier from pointer target type [-Wcast-qual] > 7 | char const( * const def )[] =( char const( * const )[] )p; > | ^> > So I'm wondering: How can I dodge this discarding the constness > warning? I'm afraid I can't help you with that. > 2nd > > Then I'm hit with another warning: > > main.c:7:3: warning: dereferencing type-punned pointer might break strict-aliasing rules [-Wstrict-aliasing] > 7 | char const( * const def )[] =( char const( * const )[] )p; > | ^~~~ > > What's this warning trying to tell me? Section 6.5p4 lists the cases where you take a pointer to an object with a given effective type, convert it to a pointer to a different type, and dereference that converted pointer with defined behavior. The conversion you do here isn't one of the ones listed. That doesn't mean that your code has undefined behavior. You only access the memory using (*def)[-3], which is an lvalue of character type, which is not only the same as the effective type of that memory, but is also one of the options listed in 6.5p4. However, in general, the result of the conversion could be used in ways that do violate 6.5p4. I don't see how you could do so in this case, because you can't use a pointer to an array type to directly access the memory - you have to subscript it to do so. There's a reason why this is a warning rather than a fatal error. > 3rd > > But the real head-scratcher is that "-3" in the last line. > On paper, we're crunching numbers to get the address of > a spot before the object (the object being "( *def )"), > so technically we're in undefined behavior territory. > > But here's the kicker - we know that spot is still chilling in the > "abc" array. Does the language standard have any loopholes that > might save our bacon here and make this not undefined after all? (*def)[-3] is defined as equivalent to *(*def - 3), so this involves subtracting an integer value from a pointer value. The relevant rules are: "If the pointer operand points to an element of an array object, and the array is large enough, the result points to an element offset from the original element such that the difference of the subscripts of the resulting and original array elements equals the integer expression. In other words, if the expression P points to the i-th element of an array object, the expressions (P)+N (equivalently, N+(P)) and (P)-N (where N has the value n) point to, respectively, the i + n-th and i − n-th elements of the array object, provided they exist. Moreover, if the expression P points to the last element of an array object, the expression (P)+1 points one past the last element of the array object, and if the expression Q points one past the last element of an array object, the expression (Q)-1 points to the last element of the array object. If the pointer operand and the result do not point to elements of the same array object or one past the last element of the array object, the behavior is undefined." (6.5.6p9) Note that the limits are based solely upon the array containing the element that the pointer points at; how the pointer itself is declared is not relevant. In this case, you can safely move that pointer as far back as the "a" in abc[], and as far forward as the the null character at the end of abc, and access the pointed-at character. You can also advance it one past the null character, but the resulting pointer value cannot be dereferenced. It can be used for pointer comparisons, which is the single most common use of one-past-the-end pointer values.