• ternary and cast lvalues in C and C++ (was Re: this girl calls c ugly)

    From Kragen Javier Sitaker@3:633/10 to All on Wed Sep 2 00:45:47 2026
    David Brown <david.brown@hesbynett.no> writes:
    There is a little information in the "discussion" page of the C++ side
    linked above. An example is

    true ? a : b = 7;

    In C, the ternary operator has higher precedence than assignment and
    this therefore parses as :

    (true ? a : b) = 7;

    In C, the ternary operator does not return an lvalue, so this is a
    constraint error.

    GCC used to support conditional lvalues as an extension to C. The GCC
    3.3.1 docs said:

    Compound expressions, conditional expressions and casts are allowed
    as lvalues provided their operands are lvalues. This means that you
    can take their addresses or store values into them.

    -- <https://gcc.gnu.org/onlinedocs/gcc-3.3.1/gcc/Lvalues.html>

    This was removed in GCC 4.0 in 02006:

    * The cast-as-lvalue, conditional-expression-as-lvalue and
    compound-expression-as-lvalue extensions, which were deprecated in
    3.3.4 and 3.4, have been removed.

    -- <https://gcc.gnu.org/gcc-4.0/changes.html>

    It?s kind of funny that they removed this extension to C shortly after
    were adding it to C++, but maybe the semantics were subtly different in
    a bug-prone way or something.

    I learned about this two years ago by trying to get the Forth
    interpreter PFE 0.9.14 to build with current GCC. PFE was largely an
    exercise in attempting to prove that you could get acceptable Forth
    performance without writing the colon interpreter in assembly, and so it aggressively used a lot of GCC extensions to C, hidden behind #ifdefs so
    that it would compile on other C compilers. It wasn't actually using conditional lvalues; it was using cast lvalues:

    #if defined __GNUC__ && !defined __STRICT_ANSI__ && !defined __cplusplus
    /* Use non-ANSI extensions avoiding address-of operator: */
    #define INC(P,T) (((T *)(P))++)

    The alternative ANSI-compliant (?) approach:

    /* Force (or fool) ANSI-C to do typecast's it normally refuses by */
    /* casting pointers, not objects, then reference the casted pointers: */
    #define INC(P,T) ((*(T **)&(P))++)

    I have no idea why you wouldn?t do that all the time. Maybe the
    optimizer in GCC 2.7.2 would throw up its hands in some sense and
    optimize worse?

    Kragen

    --- PyGate Linux v1.5.19
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)