• Re: Regarding assignment to struct

    From Rosario19@3:633/280.2 to All on Mon May 12 19:23:02 2025
    On Sun, 4 May 2025 11:01:17 +0300, Michael S wrote:

    On Sat, 3 May 2025 21:42:37 -0400
    Richard Damon <richard@damon-family.org> wrote:


    Bigger than that, and you likely want to pass the object by address,
    not by value, passing just a pointer to it.

    That sort of thinking is an example of Knutian premature optimization.

    i prefer pass memory (if it is big enought) with one address or
    reference

    --- MBSE BBS v1.1.1 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From NotAorB@3:633/280.2 to All on Tue May 13 06:38:59 2025
    Looks good!

    --- MBSE BBS v1.1.1 (Linux-x86_64)
    * Origin: None (3:633/280.2@fidonet)
  • From Andrey Tarasevich@3:633/280.2 to All on Thu May 29 22:11:01 2025
    On Mon 5/5/2025 10:20 AM, Michael S wrote:

    Here's a version of the same code that corrects the above distracting
    issues

    #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", (void *) pa, (void *) pb, (void *) pc);
    }

    This version has no UB.


    It's only not UB in the nazal demons sense.
    It's UB in a sense that we can't predict values of expressions
    like (pa==pc) and (pb==pc). I.e. pc is completely useless. In my book
    it is form of UB.

    Whether we can or cannot predict the values of `(pa==pc)` and `(pb==pc)`
    has very little impact on the usability of such expressions. The
    practical usability of such expressions is very high without relying on `(pa==pc)` and `(pb==pc)`.

    --
    Best regards,
    Andrey

    --- MBSE BBS v1.1.1 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Andrey Tarasevich@3:633/280.2 to All on Thu May 29 22:14:37 2025
    On Mon 5/5/2025 1:27 PM, Keith Thompson wrote:
    Andrey Tarasevich <noone@noone.net> writes:
    [...]
    #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", (void *) pa, (void *) pb, (void *) pc);
    }

    This version has no UB.

    I believe it does. pc points to an element of an object with
    temporary lifetime. The value of pc is then used after the object
    it points to has reached the end of its lifetime. At that point,
    pc has an indeterminate value.

    Nope. Nowhere in this code the value of `pc` is used beyond the lifetime
    of the object with temporary lifetime.

    Pay attention to the fact that the last 4 lines in above code is a
    single expression joined by a comma operator, which is the whole point
    of the corrections that differentiate it from the original version.

    N3096 6.2.4p2: "If a pointer value is used in an evaluation after
    the object the pointer points to (or just past) reaches the end of
    its lifetime, the behavior is undefined. The representation of a
    pointer object becomes indeterminate when the object the pointer
    points to (or just past) reaches the end of its lifetime."

    Not applicable in this case.

    --
    Best regards,
    Andrey


    --- MBSE BBS v1.1.1 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Andrey Tarasevich@3:633/280.2 to All on Thu May 29 22:19:13 2025
    On Tue 5/6/2025 2:35 AM, David Brown wrote:

    N3096 6.2.4p2: "If a pointer value is used in an evaluation after
    the object the pointer points to (or just past) reaches the end of
    its lifetime, the behavior is undefined. The representation of a
    pointer object becomes indeterminate when the object the pointer
    points to (or just past) reaches the end of its lifetime."


    It seems clear to me that "pc" has an indeterminate value after the expression assigning, since it points to an object with temporary lifetime.

    And attempting to use the value of an object with automatic storage
    while it has an indeterminate value is undefined behaviour.

    Again, this makes no sense. Please, pay attention to the code and the corrections made after the initial version (e.g. usage of comma
    operator). No, the value of `pc` is not indeterminate, and no, there's
    no undefined behavior in the above version of the code.

    As far as I can see, simply reading the value in "pc" to print it out is
    UB according to the C standards. It is clearly going to be a harmless operation on most hardware, but there are processors where pointer
    registers are more complicated than simple linear addresses - they can
    track some kind of segment structure describing the range of a data
    block, or permissions for access to the data, and such structures could
    have been deactivated or deallocated when the temporary lifetime object died. Even attempting to read the value of the pointer, without dereferencing it, would then cause some kind of fault or trap.

    Again, irrelevant. In the above code the temporary object does not die
    during the entire period when pointer `pc` is used in any way.

    --
    Best regards,
    Andrey



    --- MBSE BBS v1.1.1 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Andrey Tarasevich@3:633/280.2 to All on Thu May 29 22:21:04 2025
    On Tue 5/6/2025 10:36 AM, Waldek Hebisch wrote:
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    Andrey Tarasevich <noone@noone.net> writes:
    [...]
    #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", (void *) pa, (void *) pb, (void *) pc);
    }

    This version has no UB.

    I believe it does. pc points to an element of an object with
    temporary lifetime. The value of pc is then used after the object
    it points to has reached the end of its lifetime. At that point,
    pc has an indeterminate value.

    N3096 6.2.4p2: "If a pointer value is used in an evaluation after
    the object the pointer points to (or just past) reaches the end of
    its lifetime, the behavior is undefined. The representation of a
    pointer object becomes indeterminate when the object the pointer
    points to (or just past) reaches the end of its lifetime."

    Note commas above. Assignment to pc and call to printf are parts
    of a single expression, so use of pc is within lifetime of the
    temporary object.


    Exactly. I thought the nature of the corrections I made (i.e. the
    deliberate usage of comma operator) would be strikingly obvious to the participants of the thread. But alas...

    --
    Best regards,
    Andrey

    --- MBSE BBS v1.1.1 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Andrey Tarasevich@3:633/280.2 to All on Thu May 29 22:36:34 2025
    On Mon 5/5/2025 1:43 PM, Keith Thompson wrote:

    What C90 constraint does it violate? Both gcc and clang reject it
    with "-std=c90 -pedantic-errors", with an error message "ISO C90
    forbids subscripting non-lvalue array", but I don't see a relevant
    constraint in the C90 standard.


    The "constraint" in C89/90 is simply the fact that C89/90 _requires_ an
    lvalue (of array type) in order to apply array to pointer conversion.
    Here's is the original wording:

    Except when it is the operand of the sizeof operator or the unary & operator, or is a character string literal used to initialize an array
    of character type, or is a wide string literal used to initialize an
    array with element type compatible with wchar-t, an *lvalue* that has
    type “array of type” is converted to an expression that has type “pointer to rype” that points to the initial element of the array object and is not an lvalue.

    The presence of that "*lvalue*" requirement is what prevented up from
    using `[]` operator on non-lvalue arrays in C89/90, because `[]`
    critically relies on that conversion.

    In C11 the wording has changed:

    Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to
    initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to
    the initial element of the array object and is not an lvalue. If the
    array object has register storage class, the behavior is undefined.

    Note that the "lvalue" requirement has disappeared from this wording.
    That is exactly why since C99 we can apply `[]` to non-lvalue arrays.

    --
    Best regards,
    Andrey

    --- MBSE BBS v1.1.1 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Andrey Tarasevich@3:633/280.2 to All on Thu May 29 22:49:00 2025
    On Wed 5/7/2025 12:37 AM, David Brown wrote:

    That would get an immediate downcheck during review for exactly
    that reason.

    Of course. In fact, if someone presented such code for review (and
    assuming I noticed the commas!) I'd have to consider whether it was done maliciously, intentionally deceptively, due to incompetence, or smart-
    arse coding. In all my C coding experience, I can't recall ever coming across a single situation when I thought the use of the comma operator
    was appropriate in the kind of code I work with.

    Wow! That's catastrophically bad.

    As it has been stated many times before, both C and C++ are programming languages that embrace both statement-level and expression-level
    programming. Expression-level programming (e.g. where `?:` is used for branching and `,` for sequencing) is a very valuable and massively
    important programming paradigm in these languages. The fact that
    elaborate expression-level programming is not in nay way abandoned or
    shunned today is pretty obvious in C++, since C++ took major steps
    lately to develop its expression-level capabilities. But it has always
    been and will always remain important in C as well.

    The proclivity to stick exclusively to statement-level programming in C
    and, God forbid, impose it in others through so called "code reviews"...
    that would be a trait specific to "sweatshop" development outfits, which strive to replace quality with quantity. I'd agree that in a revolving
    door employment environment relying on a large number of low-competence developers such code might be seen as "too confusing". But I don't see
    why we should set our standards that low here, in `comp.lang.c`.

    --
    Best regards,
    Andrey

    --- MBSE BBS v1.1.1 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Janis Papanagnou@3:633/280.2 to All on Fri May 30 00:33:01 2025
    On 29.05.2025 14:49, Andrey Tarasevich wrote:
    On Wed 5/7/2025 12:37 AM, David Brown wrote:

    That would get an immediate downcheck during review for exactly
    that reason.

    Of course. In fact, if someone presented such code for review (and
    assuming I noticed the commas!) I'd have to consider whether it was
    done maliciously, intentionally deceptively, due to incompetence, or
    smart- arse coding. In all my C coding experience, I can't recall
    ever coming across a single situation when I thought the use of the
    comma operator was appropriate in the kind of code I work with.

    Wow! That's catastrophically bad.

    As it has been stated many times before, both C and C++ are programming languages that embrace both statement-level and expression-level
    programming. Expression-level programming (e.g. where `?:` is used for branching and `,` for sequencing) is a very valuable and massively
    important programming paradigm in these languages. The fact that
    elaborate expression-level programming is not in nay way abandoned or
    shunned today is pretty obvious in C++, since C++ took major steps
    lately to develop its expression-level capabilities. But it has always
    been and will always remain important in C as well.

    The proclivity to stick exclusively to statement-level programming in C
    and, God forbid, impose it in others through so called "code reviews"...
    that would be a trait specific to "sweatshop" development outfits, which strive to replace quality with quantity. I'd agree that in a revolving
    door employment environment relying on a large number of low-competence developers such code might be seen as "too confusing". But I don't see
    why we should set our standards that low here, in `comp.lang.c`.

    Well said.

    Janis


    --- MBSE BBS v1.1.1 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Janis Papanagnou@3:633/280.2 to All on Fri May 30 00:43:24 2025
    On 29.05.2025 14:21, Andrey Tarasevich wrote:
    On Tue 5/6/2025 10:36 AM, Waldek Hebisch wrote:
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    Andrey Tarasevich <noone@noone.net> writes:
    [...]
    #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", (void *) pa, (void *) pb, (void *) pc);
    }

    This version has no UB.

    I believe it does. pc points to an element of an object with
    temporary lifetime. The value of pc is then used after the object
    it points to has reached the end of its lifetime. At that point,
    pc has an indeterminate value.

    N3096 6.2.4p2: "If a pointer value is used in an evaluation after
    the object the pointer points to (or just past) reaches the end of
    its lifetime, the behavior is undefined. The representation of a
    pointer object becomes indeterminate when the object the pointer
    points to (or just past) reaches the end of its lifetime."

    Note commas above. Assignment to pc and call to printf are parts
    of a single expression, so use of pc is within lifetime of the
    temporary object.


    Exactly. I thought the nature of the corrections I made (i.e. the
    deliberate usage of comma operator) would be strikingly obvious to the participants of the thread. But alas...

    I wouldn't call it "strikingly obvious". Typically programmers have a abstracting look at code, and if they're used to semicolon separated
    commands the small difference between ';' and ',' may get missed (as
    some replies also indicated). Myself, as I recall from that older post,
    I did also miss it on the first glimpse. But only and after I asked
    myself what the _intentions_ the post had been I had a closer look at
    all the inconspicuous "details" and noticed the subtle difference.

    I don't think there's anything wrong with it, to be sure. But if I
    were to post such subtle differences I'd have added a comment or used
    a formatting to point and hint to that subtle but crucial difference.

    Janis


    --- MBSE BBS v1.1.1 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From James Kuyper@3:633/280.2 to All on Fri May 30 02:57:06 2025
    On Mon 5/5/2025 10:20 AM, Michael S wrote:

    Here's a version of the same code that corrects the above distracting
    issues

    #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", (void *) pa, (void *) pb, (void *) pc);
    }

    This version has no UB.


    It's only not UB in the nazal demons sense.

    UB is UB only in the nasal demons sense. UB means that "This
    international standard imposes no requirements on the behavior". That
    is, anything could happen, at least as far as the standard is concerned.
    Code with undefined behavior should not be able to produce nasal demons
    because there's no such thing as nasal demons (I think). However, if
    they did exist, producing them would not violate any requirements
    imposed by the standard, because it quite explicitly imposes none on
    such code.

    It's UB in a sense that we can't predict values of expressions
    like (pa==pc) and (pb==pc). ...

    Why not? Because of the comma operators, the lifetime of the temporary
    extends all the way till the end of the printf() call, long enough to
    make use of pc in that call safe.

    ... I.e. pc is completely useless. In my book
    it is form of UB.

    If the problem were only that there's no restrictions on the value of an expression, but that the code is otherwise safe to use, that would be
    indicated by a much weaker term: "unspecified value". Calling it "a
    form of UB" would serve no useful purpose.

    --- MBSE BBS v1.1.1 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From David Brown@3:633/280.2 to All on Fri May 30 05:05:37 2025
    On 29/05/2025 14:19, Andrey Tarasevich wrote:
    On Tue 5/6/2025 2:35 AM, David Brown wrote:

    N3096 6.2.4p2: "If a pointer value is used in an evaluation after
    the object the pointer points to (or just past) reaches the end of
    its lifetime, the behavior is undefined. The representation of a
    pointer object becomes indeterminate when the object the pointer
    points to (or just past) reaches the end of its lifetime."


    It seems clear to me that "pc" has an indeterminate value after the
    expression assigning, since it points to an object with temporary
    lifetime.

    And attempting to use the value of an object with automatic storage
    while it has an indeterminate value is undefined behaviour.

    Again, this makes no sense. Please, pay attention to the code and the corrections made after the initial version (e.g. usage of comma
    operator). No, the value of `pc` is not indeterminate, and no, there's
    no undefined behavior in the above version of the code.

    As far as I can see, simply reading the value in "pc" to print it out
    is UB according to the C standards. It is clearly going to be a
    harmless operation on most hardware, but there are processors where
    pointer registers are more complicated than simple linear addresses -
    they can track some kind of segment structure describing the range of
    a data block, or permissions for access to the data, and such
    structures could have been deactivated or deallocated when the
    temporary lifetime object died. Even attempting to read the value of
    the pointer, without dereferencing it, would then cause some kind of
    fault or trap.

    Again, irrelevant. In the above code the temporary object does not die during the entire period when pointer `pc` is used in any way.


    I posted later that I had made a mistake, and not noticed the use of the
    comma instead of a semicolon.

    Why are you dredging up an outdated thread? I made an error three weeks
    ago, and realised the mistake shortly afterwards. What do you expect me
    or anyone else to learn from that now, after all this time?


    --- MBSE BBS v1.1.1 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From David Brown@3:633/280.2 to All on Fri May 30 05:20:59 2025
    On 29/05/2025 14:49, Andrey Tarasevich wrote:
    On Wed 5/7/2025 12:37 AM, David Brown wrote:

    That would get an immediate downcheck during review for exactly
    that reason.

    Of course. In fact, if someone presented such code for review (and
    assuming I noticed the commas!) I'd have to consider whether it was
    done maliciously, intentionally deceptively, due to incompetence, or
    smart- arse coding. In all my C coding experience, I can't recall
    ever coming across a single situation when I thought the use of the
    comma operator was appropriate in the kind of code I work with.

    Wow! That's catastrophically bad.

    As it has been stated many times before, both C and C++ are programming languages that embrace both statement-level and expression-level programming. Expression-level programming (e.g. where `?:` is used for branching and `,` for sequencing) is a very valuable and massively
    important programming paradigm in these languages. The fact that
    elaborate expression-level programming is not in nay way abandoned or shunned today is pretty obvious in C++, since C++ took major steps
    lately to develop its expression-level capabilities. But it has always
    been and will always remain important in C as well.

    No, expression-level programming has always been and will likely always
    remain a very minor part of C programming. Yes, some people make use of
    the comma operator. Some people do so extensively - and they are often,
    but not necessarily, considered "smart-arse" programmers rather than
    "smart" programmers. If the comma operator were removed from the C
    language, I guess some 95% of programmers would barely notice - at
    worst, they would have to add an extra line inside an occasional "for"
    loop. (The tertiary operator is used much more.)

    I did not say that the use of comma operators is always bad - I said I
    do not recall seeing it in the kind of code I work with in a situation
    where I thought it was a good way to write the code. A significant part
    of that is the kind of code I work with - in code for small systems
    where high reliability and safety is vital, code clarity is of utmost importance. Code that does not do what it first appears to do is
    severely frowned upon. Code is written in a very imperative style.

    In my world, code that uses "malloc" is rarely acceptable, and for most programs, "double" is very seldom an appropriate choice of type. But
    that does not mean these are not usable for other kinds of C
    programming. There are many reasons why different styles of coding are
    used in different circumstances.

    Even when C++ is used, with its significantly broader support for a
    variety of programming paradigms, I do not recall seeing the comma
    operator used.


    The proclivity to stick exclusively to statement-level programming in C
    and, God forbid, impose it in others through so called "code reviews"... that would be a trait specific to "sweatshop" development outfits, which strive to replace quality with quantity. I'd agree that in a revolving
    door employment environment relying on a large number of low-competence developers such code might be seen as "too confusing". But I don't see
    why we should set our standards that low here, in `comp.lang.c`.


    I don't quite see how you are in any position to judge the coding styles
    used by people you know nothing about, working in fields that you know
    nothing about.

    I am happy that different types of programming styles and paradigms are
    used for different purposes - imperative C is not suitable for most
    coding tasks. Equally, expression-style programming is not appropriate
    for all coding tasks.

    However, one thing that is never suitable for any real-world programming
    is deceptive code that is not what it appears to be.


    --- MBSE BBS v1.1.1 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Keith Thompson@3:633/280.2 to All on Fri May 30 06:56:38 2025
    Andrey Tarasevich <noone@noone.net> writes:
    On Mon 5/5/2025 1:27 PM, Keith Thompson wrote:
    Andrey Tarasevich <noone@noone.net> writes:
    [...]
    #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", (void *) pa, (void *) pb, (void *) pc);
    }

    This version has no UB.
    I believe it does. pc points to an element of an object with
    temporary lifetime. The value of pc is then used after the object
    it points to has reached the end of its lifetime. At that point,
    pc has an indeterminate value.

    Nope. Nowhere in this code the value of `pc` is used beyond the
    lifetime of the object with temporary lifetime.

    Pay attention to the fact that the last 4 lines in above code is a
    single expression joined by a comma operator, which is the whole point
    of the corrections that differentiate it from the original version.

    Yes, yes, the fact that the code uses comma operators rather
    than semicolons was pointed out and discussed several weeks ago.
    I initially missed that change, it was pointed out, I acknowledged
    it, and we moved on.

    If you're going to reply to an old article, please read the thread
    before posting.

    In response to something else you recently wrote in this thread,
    the change from semicolons to commas was not at all obvious.
    The code is formatted in a way that strongly suggests statements
    with ending semicolons. Several of us missed the commas on first
    reading, having seen nearly identical code earlier in the thread
    with no acknowledgement that the code had been modified. I simply
    assumed that it was the same code, and didn't bother to re-read it
    character by character.

    [...]

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */

    --- MBSE BBS v1.1.1 (Linux-x86_64)
    * Origin: None to speak of (3:633/280.2@fidonet)
  • From Scott Lurndal@3:633/280.2 to All on Fri May 30 07:15:56 2025
    Reply-To: slp53@pacbell.net

    David Brown <david.brown@hesbynett.no> writes:
    On 29/05/2025 14:49, Andrey Tarasevich wrote:
    On Wed 5/7/2025 12:37 AM, David Brown wrote:

    That would get an immediate downcheck during review for exactly
    that reason.

    Of course.  In fact, if someone presented such code for review (and
    assuming I noticed the commas!) I'd have to consider whether it was
    done maliciously, intentionally deceptively, due to incompetence, or
    smart- arse coding.  In all my C coding experience, I can't recall
    ever coming across a single situation when I thought the use of the
    comma operator was appropriate in the kind of code I work with.

    Wow! That's catastrophically bad.

    As it has been stated many times before, both C and C++ are programming
    languages that embrace both statement-level and expression-level
    programming. Expression-level programming (e.g. where `?:` is used for
    branching and `,` for sequencing) is a very valuable and massively
    important programming paradigm in these languages. The fact that
    elaborate expression-level programming is not in nay way abandoned or
    shunned today is pretty obvious in C++, since C++ took major steps
    lately to develop its expression-level capabilities. But it has always
    been and will always remain important in C as well.

    No, expression-level programming has always been and will likely always >remain a very minor part of C programming. Yes, some people make use of
    the comma operator. Some people do so extensively - and they are often,
    but not necessarily, considered "smart-arse" programmers rather than
    "smart" programmers. If the comma operator were removed from the C >language, I guess some 95% of programmers would barely notice - at
    worst, they would have to add an extra line inside an occasional "for"
    loop. (The tertiary operator is used much more.)

    And sometimes, excessive use of the comma operator causes
    compiler failures.

    cfront generated the comma operator extensively, and expression trees
    would grow to very large sizes. There was a bug in PCC (for the
    88100) where it would run out of temporary registers while generating
    code for some cfront generated comma expressions (which were -far- from
    human readable). I had to fix the temporary register allocation
    code in PCC to spill registers when the sethi-ullman number for an
    expression exceeded the number of registers.

    That was circa 1990, and I've generally not found any arguments
    favoring their general use persuasive in the years since, including
    Andrey's and Kaz's responses recently posted here.

    The simple fact that experienced programmers that read this usenet
    newsgroup missed the comma operators in the original example speaks
    volumes.



    --- MBSE BBS v1.1.1 (Linux-x86_64)
    * Origin: UsenetServer - www.usenetserver.com (3:633/280.2@fidonet)
  • From Keith Thompson@3:633/280.2 to All on Fri May 30 07:36:53 2025
    Andrey Tarasevich <noone@noone.net> writes:
    On Mon 5/5/2025 1:43 PM, Keith Thompson wrote:
    What C90 constraint does it violate? Both gcc and clang reject it
    with "-std=c90 -pedantic-errors", with an error message "ISO C90
    forbids subscripting non-lvalue array", but I don't see a relevant
    constraint in the C90 standard.

    The "constraint" in C89/90 is simply the fact that C89/90 _requires_
    an lvalue (of array type) in order to apply array to pointer
    conversion. Here's is the original wording:

    Except when it is the operand of the sizeof operator or the unary &
    operator, or is a character string literal used to initialize an
    array of character type, or is a wide string literal used to
    initialize an array with element type compatible with wchar-t, an
    *lvalue* that has type “array of type” is converted to an expression
    that has type “pointer to rype” that points to the initial element
    of the array object and is not an lvalue.

    The presence of that "*lvalue*" requirement is what prevented up from
    using `[]` operator on non-lvalue arrays in C89/90, because `[]`
    critically relies on that conversion.

    In C11 the wording has changed:

    Except when it is the operand of the sizeof operator, the _Alignof
    operator, or the unary & operator, or is a string literal used to
    initialize an array, an expression that has type ‘‘array of type’’
    is converted to an expression with type ‘‘pointer to type’’ that
    points to the initial element of the array object and is not an
    lvalue. If the array object has register storage class, the behavior
    is undefined.

    Note that the "lvalue" requirement has disappeared from this
    wording. That is exactly why since C99 we can apply `[]` to non-lvalue arrays.

    Yes, this was discussed in this thread several weeks ago.

    A quibble: There are no implicit constraints. All constraints
    are in sections clearly marked "Constraints". I believe that the
    C90 constraint that's violated by `(a = b).a[5]` is the one that
    requires the operands of [] to have pointer and integer types.
    Since `(a = b).a` does not undergo array-to-pointer conversion
    *in C90*, it's of array type.

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */

    --- MBSE BBS v1.1.1 (Linux-x86_64)
    * Origin: None to speak of (3:633/280.2@fidonet)
  • From Keith Thompson@3:633/280.2 to All on Fri May 30 07:54:53 2025
    scott@slp53.sl.home (Scott Lurndal) writes:
    [...]
    And sometimes, excessive use of the comma operator causes
    compiler failures.

    cfront generated the comma operator extensively, and expression trees
    would grow to very large sizes. There was a bug in PCC (for the
    88100) where it would run out of temporary registers while generating
    code for some cfront generated comma expressions (which were -far- from
    human readable). I had to fix the temporary register allocation
    code in PCC to spill registers when the sethi-ullman number for an
    expression exceeded the number of registers.

    That was circa 1990, and I've generally not found any arguments
    favoring their general use persuasive in the years since, including
    Andrey's and Kaz's responses recently posted here.

    So a compiler you used circa 1990 had problems with comma expressions.

    That's hardly an argument against using comma operators today.

    The simple fact that experienced programmers that read this usenet
    newsgroup missed the comma operators in the original example speaks
    volumes.

    *That's* a valid argument.

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */

    --- MBSE BBS v1.1.1 (Linux-x86_64)
    * Origin: None to speak of (3:633/280.2@fidonet)
  • From David Brown@3:633/280.2 to All on Fri May 30 18:50:56 2025
    On 29/05/2025 23:15, Scott Lurndal wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 29/05/2025 14:49, Andrey Tarasevich wrote:
    On Wed 5/7/2025 12:37 AM, David Brown wrote:

    That would get an immediate downcheck during review for exactly
    that reason.

    Of course. In fact, if someone presented such code for review (and
    assuming I noticed the commas!) I'd have to consider whether it was
    done maliciously, intentionally deceptively, due to incompetence, or
    smart- arse coding. In all my C coding experience, I can't recall
    ever coming across a single situation when I thought the use of the
    comma operator was appropriate in the kind of code I work with.

    Wow! That's catastrophically bad.

    As it has been stated many times before, both C and C++ are programming
    languages that embrace both statement-level and expression-level
    programming. Expression-level programming (e.g. where `?:` is used for
    branching and `,` for sequencing) is a very valuable and massively
    important programming paradigm in these languages. The fact that
    elaborate expression-level programming is not in nay way abandoned or
    shunned today is pretty obvious in C++, since C++ took major steps
    lately to develop its expression-level capabilities. But it has always
    been and will always remain important in C as well.

    No, expression-level programming has always been and will likely always
    remain a very minor part of C programming. Yes, some people make use of
    the comma operator. Some people do so extensively - and they are often,
    but not necessarily, considered "smart-arse" programmers rather than
    "smart" programmers. If the comma operator were removed from the C
    language, I guess some 95% of programmers would barely notice - at
    worst, they would have to add an extra line inside an occasional "for"
    loop. (The tertiary operator is used much more.)

    And sometimes, excessive use of the comma operator causes
    compiler failures.

    That is also an issue in the world of small-systems embedded programming.

    While a lot of it these days is on ARM, and most of that is done using
    gcc, there are hundreds of C compilers of varying quality (and price,
    which is no indication of quality) for embedded systems. Many of these
    other toolchains have bugs, non-conformities, inconsistencies and
    weaknesses. (gcc is not perfect either!) People programming for 8-bit
    and 16-bit microcontrollers using such tools will - and should - use a conservative subset of the C language. Obscure and rarely used features
    of the language will be avoided, and code will be written in a simpler
    manner. You don't write code in a way that increases the risk of
    catching a bug in a poorly tested part of the compiler, or in a way that
    might lead to unexpectedly inefficient results.

    With 32-bit ARM now dominating the industry, along with more reliable
    tools (primarily gcc, with clang a distant second), there is much less
    need to pander to flaws in the toolchain but you still need to consider weaknesses in humans. When you are writing software that is intended to
    run continuously without problems for years, or where a misunderstanding
    by a new maintainer a decade later can lead to safety risks, you don't
    write "cool" code!

    It is well known that "Debugging is twice as hard as writing the code in
    the first place. Therefore, if you write the code as cleverly as
    possible, you are, by definition, not smart enough to debug it." I
    would also say that understanding and maintaining other people's code is
    often a lot more than twice as hard as writing the code yourself. I aim
    to code accordingly.


    cfront generated the comma operator extensively, and expression trees
    would grow to very large sizes. There was a bug in PCC (for the
    88100) where it would run out of temporary registers while generating
    code for some cfront generated comma expressions (which were -far- from
    human readable). I had to fix the temporary register allocation
    code in PCC to spill registers when the sethi-ullman number for an
    expression exceeded the number of registers.

    That was circa 1990, and I've generally not found any arguments
    favoring their general use persuasive in the years since, including
    Andrey's and Kaz's responses recently posted here.

    The simple fact that experienced programmers that read this usenet
    newsgroup missed the comma operators in the original example speaks
    volumes.


    Of course people are experienced in different things - "programming",
    even when limited to a single language, is a broad field.


    --- MBSE BBS v1.1.1 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Scott Lurndal@3:633/280.2 to All on Sat May 31 00:29:22 2025
    Reply-To: slp53@pacbell.net

    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    scott@slp53.sl.home (Scott Lurndal) writes:
    [...]
    And sometimes, excessive use of the comma operator causes
    compiler failures.

    cfront generated the comma operator extensively, and expression trees
    would grow to very large sizes. There was a bug in PCC (for the
    88100) where it would run out of temporary registers while generating
    code for some cfront generated comma expressions (which were -far- from
    human readable). I had to fix the temporary register allocation
    code in PCC to spill registers when the sethi-ullman number for an
    expression exceeded the number of registers.

    That was circa 1990, and I've generally not found any arguments
    favoring their general use persuasive in the years since, including
    Andrey's and Kaz's responses recently posted here.

    So a compiler you used circa 1990 had problems with comma expressions.

    That's hardly an argument against using comma operators today.

    Tru, was an anecdote, not an argument, which conditioned
    my opinion of comma operators.


    The simple fact that experienced programmers that read this usenet
    newsgroup missed the comma operators in the original example speaks
    volumes.

    *That's* a valid argument.

    Indeed.


    --- MBSE BBS v1.1.1 (Linux-x86_64)
    * Origin: UsenetServer - www.usenetserver.com (3:633/280.2@fidonet)
  • From Tim Rentsch@3:633/280.2 to All on Sat Jun 7 10:44:14 2025
    Andrey Tarasevich <noone@noone.net> writes:

    On Tue 5/6/2025 10:36 AM, Waldek Hebisch wrote:

    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:

    Andrey Tarasevich <noone@noone.net> writes:
    [...]

    #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", (void *) pa, (void *) pb, (void *) pc);
    }

    This version has no UB.

    I believe it does. pc points to an element of an object with
    temporary lifetime. The value of pc is then used after the object
    it points to has reached the end of its lifetime. At that point,
    pc has an indeterminate value.

    N3096 6.2.4p2: "If a pointer value is used in an evaluation after
    the object the pointer points to (or just past) reaches the end of
    its lifetime, the behavior is undefined. The representation of a
    pointer object becomes indeterminate when the object the pointer
    points to (or just past) reaches the end of its lifetime."

    Note commas above. Assignment to pc and call to printf are parts
    of a single expression, so use of pc is within lifetime of the
    temporary object.

    Exactly. I thought the nature of the corrections I made (i.e. the
    deliberate usage of comma operator) would be strikingly obvious to the participants of the thread. But alas...

    My own reaction is that the changes were not by themselves
    strikingly obvious. But in combination with the explicit
    statement that "This version has no UB" it seems obvious
    enough.

    --- MBSE BBS v1.1.1 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)