| Deutsch English Français Italiano |
|
<vvauc7$vqld$1@dont-email.me> View for Bookmarking (what is this?) Look up another Usenet article |
Path: ...!eternal-september.org!feeder3.eternal-september.org!news.eternal-september.org!eternal-september.org!.POSTED!not-for-mail
From: David Brown <david.brown@hesbynett.no>
Newsgroups: comp.lang.c
Subject: Re: Regarding assignment to struct
Date: Mon, 5 May 2025 20:00:39 +0200
Organization: A noiseless patient Spider
Lines: 148
Message-ID: <vvauc7$vqld$1@dont-email.me>
References: <vv338b$16oam$1@dont-email.me> <vv4j9p$33vhj$1@dont-email.me>
<86plgo7ahu.fsf@linuxsc.com> <vv9hu7$3nomg$1@dont-email.me>
<86selj3y3b.fsf@linuxsc.com>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8; format=flowed
Content-Transfer-Encoding: 7bit
Injection-Date: Mon, 05 May 2025 20:00:41 +0200 (CEST)
Injection-Info: dont-email.me; posting-host="247a2d0f921fb74802e134777cfd937d";
logging-data="1043117"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX18Bokj/IXbC/yQ/oNLY3WiY1E8FORZMsd4="
User-Agent: Mozilla Thunderbird
Cancel-Lock: sha1:u6q/PaZtup3lHqIOxGyH964nTPw=
In-Reply-To: <86selj3y3b.fsf@linuxsc.com>
Content-Language: en-GB
Bytes: 7415
On 05/05/2025 16:56, Tim Rentsch wrote:
> Andrey Tarasevich <noone@noone.net> writes:
>
>> On Sun 5/4/2025 6:48 AM, Tim Rentsch wrote:
>>
>>>> One dark corner this feature has, is that in C (as opposed to C++) the
>>>> result of an assignment operator is an rvalue, which can easily lead
>>>> to some interesting consequences related to structs with arrays
>>>> inside.
>>>
>>> I'm curious to know what interesting consequences you mean here. Do
>>> you mean something other than cases that have undefined behavior?
>>
>> I'm referring to the matter of the address identity of the resultant
>> rvalue object. At first, "address identity of rvalue" might sound
>> strange, but the standard says that there's indeed an object tied to
>> such rvalue, and once we start applying array-to-pointer conversion
>> (and use `[]` operator), lvalues and addresses quickly come into the
>> picture.
>>
>> The standard says in 6.2.4/8:
>>
>> "A non-lvalue expression with structure or union type, where the
>> structure or union contains a member with array type [...]
>> refers to an object with automatic storage duration and temporary
>> lifetime. Its lifetime begins when the expression is evaluated and its
>> initial value is the value of the expression. Its lifetime ends when
>> the evaluation of the containing full expression ends. [...] Such an
>> object need not have a unique address."
>> https://port70.net/~nsz/c/c11/n1570.html#6.2.4p8
>
> The last sentence there is not present in N1570. Apparently it was
> introduced later, in C17. (My appreciation to Keith Thompson for
> reporting this.)
>
>> I wondering what the last sentence is intended to mean ("... need not
>> have a unique address"). At the first sight, the intent seems to be
>> obvious: it simply says that such temporary objects might repeatedly
>> appear (and disappear) at the same location in storage, which is a
>> natural thing to expect.
>
> Ahh, I see now what your concern is.
>
>> But is it, perhaps, intended to also allow such temporaries to have
>> addresses identical to regular named objects? It is not immediately
>> clear to me.
>
> My reading of the post-C11 standards is that they allow the "new"
> object to overlap with already existing objects, including both
> declared objects and objects whose storage was allocated using
> malloc().
>
>> And when I make the following experiment with GCC and Clang
>>
>> #include <stdio.h>
>>
>> struct S { int a[10]; };
>>
>> int main()
>> {
>> struct S a, b = { 0 };
>> int *pa, *pb, *pc;
>>
>> pa = &a.a[5];
>> pb = &b.a[5];
>> pc = &(a = b).a[5];
>>
>> printf("%p %p %p\n", pa, pb, pc);
>> }
>>
>> I consistently get the following output from GCC
>>
>> 0x7fff73eb5544 0x7fff73eb5574 0x7fff73eb5544
>>
>> And this is what I get from Clang
>>
>> 0x7ffd2b8dbf44 0x7ffd2b8dbf14 0x7ffd2b8dbee4
>>
>> As you can see, GCC apparently took C++-like approach to this
>> situation. The returned "temporary" is not really a separate temporary
>> at all, but actually `a` itself.
>
> Yeah.
>
>> Meanwhile, in Clang all three pointers are different, i.e. Clang
>> decided to actually create a separate temporary object for the result
>> of the assignment.
>
> Which in my reading of the standard is required under C11 rules.
> I have reproduced your results under -std=c11 -pedantic, for both
> gcc and clang.
>
Compilers don't have to follow the behaviour specified by the standard
in a "direct translation" manner in order to be correct and conforming.
They have to generate code that in the absence of any attempt to execute
something with undefined behaviour, will give the same observable
behaviour as a "direct translation" would.
The result of the "(a = b)" expression should be a temporary object
distinct from "a" and "b", with a lifetime extending only to the end of
the expression assigning to "pc" (prior to C17).
Is there any way to distinguish between "pc" pointing to an int inside
this now dead temporary object, and it pointing to an int inside "a",
without invoking undefined behaviour?
By the time you are using "pc" to print it, the pointer itself has an
indeterminate value - the compiler can quite happily give it the same
value as "pa", so looking at the pointer in the printf() statement does
not show a non-conformance.
Attempting to modify the temporary lifetime object, such as by writing
"*(pc = &(a = b).a[5]) = 42;", is undefined behaviour.
It is entirely possible that there /is/ some way to determine that the
compiler is not making a distinct temporary object while avoiding any
undefined behaviour or indeterminate values. But I don't think the code
here does show that - and it is therefore not an example of
non-conforming behaviour. I think GCC and clang can be viewed as having
simply picked different ways to generate their indeterminate values.
I will be happy to change that opinion if someone has a better argument
or example.
>> I have a strong feeling that GCC's behavior is non-conforming. The
>> last sentence of 6.2.4/8 is not supposed to permit "projecting" the
>> resultant temporaries onto existing named objects. I could be wrong...
>
> My judgment is that the behavior under gcc is non-conforming if the
> compilation was done using C11 semantics. Under C17 or later rules
> the gcc behavior is allowed (and may have been what prompted the
> change in C17, but that is just speculation on my part). In any
> case I understand now what you were getting at. Thank you for
> bringing this hazard to the group's attention.
>
> I hope someone files a bug report for gcc using -std=c11 rules,
> because what gcc does under that setting (along with -pedantic)
> is surely at odds with the plain reading of the C11 standard,
> for the situation being discussed here.
>
> Editorial comment: here is yet another case where post-C11 changes
> to the C standard seem ill advised, and another reason not to use
> any version of the ISO C standard for C17 or later. And it's
> disappointing that gcc -std=c11 -pedantic strays into the realm of
> non-conforming behavior.