In how many ways should this fail?

Discussion in 'C Programming' started by Anders Wegge Keller, Jan 30, 2012.

  1. At work, we got into a talk about weird C constructs. One of my
    collegues volunteered this line:

    (a>b)?a:b = 42;

    According to him, he have found three different compilers with three
    different ways of handling this. One did nothing, one always assigned
    to b, and the last one did what one should expect, were it legal.

    We ended up discussing what the acutal problem here is. The
    diffrerent compilers we had at hand gave different diagnostics, so
    they giv no clue. We narrowed it down to one of two:

    1) a and b are not lvalues in this context.

    2) This expression invokes undefined behaviour, since there are no
    sequence point between (a>b) and the assignment.



    --
    /Wegge

    Leder efter redundant peering af dk.*,linux.debian.*
     
    Anders Wegge Keller, Jan 30, 2012
    #1
    1. Advertising

  2. Anders Wegge Keller

    James Kuyper Guest

    On 01/30/2012 10:32 AM, Anders Wegge Keller wrote:
    >
    > At work, we got into a talk about weird C constructs. One of my
    > collegues volunteered this line:
    >
    > (a>b)?a:b = 42;
    >
    > According to him, he have found three different compilers with three
    > different ways of handling this. One did nothing, one always assigned
    > to b, and the last one did what one should expect, were it legal.
    >
    > We ended up discussing what the acutal problem here is. The
    > diffrerent compilers we had at hand gave different diagnostics, so
    > they giv no clue. We narrowed it down to one of two:
    >
    > 1) a and b are not lvalues in this context.


    That's true, but not relevant. The key issue is that the C standard
    fails to specify that it's an lvalue - but to make it clear that this
    wasn't an oversight, footnote 95 says "A conditional expression does not
    yield an lvalue." As a result, its result cannot be assigned to. Note
    that this is one of the differences between C and C++; the result of a
    ?: expression is an lvalue in C++.

    > 2) This expression invokes undefined behaviour, since there are no
    > sequence point between (a>b) and the assignment.


    6.5.15 "Conditional operator" p4: "The first operand is evaluated; there
    is a sequence point after its evaluation." Since you need to evaluate
    the ?: expression to determine what should be assigned to; there is
    indeed a sequence point between the evaluation of the > operation and
    the assignment. If you'd avoided the other problem by writing

    *((a>b)?&a : &b) = 42;

    there would not have been a problem.
     
    James Kuyper, Jan 30, 2012
    #2
    1. Advertising

  3. Anders Wegge Keller

    James Kuyper Guest

    On 01/30/2012 11:07 AM, James Kuyper wrote:
    > On 01/30/2012 10:32 AM, Anders Wegge Keller wrote:
    >>
    >> At work, we got into a talk about weird C constructs. One of my
    >> collegues volunteered this line:
    >>
    >> (a>b)?a:b = 42;

    ....
    >> 1) a and b are not lvalues in this context.

    >
    > That's true, but not relevant. The key issue is that the C standard
    > fails to specify that it's an lvalue


    In the course of editing that sentence, I accidentally removed the thing
    that "it" refers to. That sentence should have said: "The key issue is
    that the C standard fails to specify that the result of a conditional
    expression is an lvalue."
     
    James Kuyper, Jan 30, 2012
    #3
  4. Anders Wegge Keller

    gwowen Guest

    On Jan 30, 3:32 pm, Anders Wegge Keller <> wrote:

    >  According to him, he have found three different compilers with three
    > different ways of handling this. One did nothing, one always assigned
    > to b, and the last one did what one should expect, were it legal.


    What should one expect?

    (a>b) ? a : (b=42);

    In C (given that [foo() ? a : b] isn't an lvalue), that seems a
    perfectly sensible parse to me. One might even do

    x = ((a>b) ? a : (b=42));

    as a truly horrible way of writing

    if(a>b) {
    x = a;
    } else {
    x = b = 42;
    }

    In C++, obviously
    (a > b ? a : b) = 42;
    is a reasonable parse.

    As ever, anyone who fails to use enough parenthesis to unambiguously
    express intent, needs a good hard slap.
     
    gwowen, Jan 30, 2012
    #4
  5. Anders Wegge Keller

    BartC Guest

    "Devil with the China Blue Dress" <> wrote in message
    news:-september.org...
    > In article <>,
    > Anders Wegge Keller <> wrote:
    >
    >> At work, we got into a talk about weird C constructs. One of my
    >> collegues volunteered this line:
    >>
    >> (a>b)?a:b = 42;

    >
    > *(a>b ? &a : &b) = 42;


    There's no reason why the original version shouldn't work. Doing as you
    suggest is a bit like writing:

    *(&a) = 42;

    instead of a = 42. It shouldn't be necessary and it's less readable.

    --
    Bartc
     
    BartC, Jan 30, 2012
    #5
  6. Anders Wegge Keller

    timprince Guest

    On 1/30/2012 10:32 AM, Anders Wegge Keller wrote:
    >
    > At work, we got into a talk about weird C constructs. One of my
    > collegues volunteered this line:
    >
    > (a>b)?a:b = 42;
    >
    > According to him, he have found three different compilers with three
    > different ways of handling this. One did nothing, one always assigned
    > to b, and the last one did what one should expect, were it legal.
    >
    > We ended up discussing what the acutal problem here is. The
    > diffrerent compilers we had at hand gave different diagnostics, so
    > they giv no clue. We narrowed it down to one of two:
    >
    > 1) a and b are not lvalues in this context.
    >
    > 2) This expression invokes undefined behaviour, since there are no
    > sequence point between (a>b) and the assignment.
    >
    >
    >

    You may require additional parentheses to use this extension more
    reliably (and force diagnostics when it is interpreted as a do-nothing):
    ((a>b)?a:b) = 42;
    Under compilers which support this extension, you would need to set
    standards checking options to reject it and get a complaint. Other
    compilers I have used will reject it with a diagnostic.

    --
    Tim Prince
     
    timprince, Jan 30, 2012
    #6
  7. Anders Wegge Keller

    Kaz Kylheku Guest

    On 2012-01-30, Anders Wegge Keller <> wrote:
    >
    > At work, we got into a talk about weird C constructs. One of my
    > collegues volunteered this line:
    >
    > (a>b)?a:b = 42;
    >
    > According to him, he have found three different compilers with three
    > different ways of handling this.


    Some compilers accept a nonstandard dialect with extensions by default and have
    to be told to interpret the input language as ISO C.

    In the GNU C dialect, it is possible assign "through" the ternary operator.

    In the C++ dialect of C, you can do this also, so if you compile code as either
    C or C++, you may run into this working under C++, but then breaking when
    someone runs the C build.

    ISO C does not have this feature: the result of A ? B : C is not an lvalue.

    Very recent case in point: http://www.kylheku.com/cgit/txr/commit/?id=5ae4654a4c6961d35b9bb4833934c51f17936df6

    > We ended up discussing what the acutal problem here is.


    Probably, that of not knowing that there exist different dialects of C, and that
    your compiler might not be using the one you think it is.
     
    Kaz Kylheku, Jan 30, 2012
    #7
  8. Anders Wegge Keller

    Kaz Kylheku Guest

    On 2012-01-30, BartC <> wrote:
    >
    >
    > "Devil with the China Blue Dress" <> wrote in message
    > news:-september.org...
    >> In article <>,
    >> Anders Wegge Keller <> wrote:
    >>
    >>> At work, we got into a talk about weird C constructs. One of my
    >>> collegues volunteered this line:
    >>>
    >>> (a>b)?a:b = 42;

    >>
    >> *(a>b ? &a : &b) = 42;

    >
    > There's no reason why the original version shouldn't work. Doing as you
    > suggest is a bit like writing:
    >
    > *(&a) = 42;
    >
    > instead of a = 42. It shouldn't be necessary and it's less readable.


    It isn't necessary in the "better C" known as C++. Why not use that?
    Ah right, portability.

    The *(a>b ? &a : &b) = 42; method works in both C and C++.

    The ternary syntax is awful anyway. Any time you nest it or combine it
    with adjacent operators it becomes hard to decipher the precedence.

    A function-like syntax both problems:

    #define IF(a,b,c) ((a) ? (b) : (c)))
    #define IFL(a,b,c) (*((a) ? &(b) : &(c)))

    IFL(foo != bar(), a, b) = x | FLAG;
     
    Kaz Kylheku, Jan 30, 2012
    #8
  9. Anders Wegge Keller

    James Kuyper Guest

    On 01/30/2012 11:43 AM, BartC wrote:
    >
    >
    > "Devil with the China Blue Dress" <> wrote in message
    > news:-september.org...
    >> In article <>,
    >> Anders Wegge Keller <> wrote:
    >>
    >>> At work, we got into a talk about weird C constructs. One of my
    >>> collegues volunteered this line:
    >>>
    >>> (a>b)?a:b = 42;

    >>
    >> *(a>b ? &a : &b) = 42;

    >
    > There's no reason why the original version shouldn't work. ...


    That depends upon what you mean by "work". The original expression is
    equivalent to

    b = 42;

    Which is unlikely to be what the author intended, and would be a pretty
    bizarre way of writing it if that was what he intended. Assuming that
    the parentheses needed to force the intended parse are inserted, there's
    still the problem that ((a>b)?a:b) isn't an lvalue.

    > ... Doing as you
    > suggest is a bit like writing:
    >
    > *(&a) = 42;
    >
    > instead of a = 42. ...


    There's one key difference: *((a>b) ? &a : &b) = 42 is necessary in C,
    *(&a) = 42 is not.

    > ... It shouldn't be necessary and it's less readable.


    You're arguably correct about that. C++ allows the original expression
    to work as intended, even though C does not. This is because the C++
    grammar differs from the C grammar by not allowing the third operand to
    be an assignment expression, and the C++ standard specifies that the
    result is an lvalue. They made the change because it makes some things
    involving C++ classes are more convenient; but the fact that it has been
    done is C++ means it could be done in C - that fact doesn't depend upon
    any C++-specific features.
     
    James Kuyper, Jan 30, 2012
    #9
  10. gwowen <> writes:

    > On Jan 30, 3:32 pm, Anders Wegge Keller <> wrote:
    >
    >>  According to him, he have found three different compilers with three
    >> different ways of handling this. One did nothing, one always assigned
    >> to b, and the last one did what one should expect, were it legal.


    <talking about:>

    a > b ? a : b = 42;

    > What should one expect?
    >
    > (a>b) ? a : (b=42);
    >
    > In C (given that [foo() ? a : b] isn't an lvalue), that seems a
    > perfectly sensible parse to me.


    It might be sensible, but it's no what C's syntax mandates. A
    conforming compiler must parse it as

    ((a > b) ? a : b) = 42;

    <snip>

    > In C++, obviously
    > (a > b ? a : b) = 42;
    > is a reasonable parse.


    Again, it's reasonable... but wrong. The original expression must parse
    as

    (a > b) ? a : (b = 42);

    The differences between C and C++ here are two-fold: not only can a
    conditional expression be an l-value, but the syntax is also different.
    An assignment is valid after the ':' in C++ but not in C.

    Lots of operator precedence tables that claim be for "C/C++" get this
    wrong.

    > As ever, anyone who fails to use enough parenthesis to unambiguously
    > express intent, needs a good hard slap.


    Particularly in this case!

    --
    Ben.
     
    Ben Bacarisse, Jan 30, 2012
    #10
  11. Anders Wegge Keller

    Kaz Kylheku Guest

    On 2012-01-30, timprince <> wrote:
    > On 1/30/2012 10:32 AM, Anders Wegge Keller wrote:
    >>
    >> At work, we got into a talk about weird C constructs. One of my
    >> collegues volunteered this line:
    >>
    >> (a>b)?a:b = 42;
    >>
    >> According to him, he have found three different compilers with three
    >> different ways of handling this. One did nothing, one always assigned
    >> to b, and the last one did what one should expect, were it legal.
    >>
    >> We ended up discussing what the acutal problem here is. The
    >> diffrerent compilers we had at hand gave different diagnostics, so
    >> they giv no clue. We narrowed it down to one of two:
    >>
    >> 1) a and b are not lvalues in this context.
    >>
    >> 2) This expression invokes undefined behaviour, since there are no
    >> sequence point between (a>b) and the assignment.
    >>
    >>
    >>

    > You may require additional parentheses to use this extension more
    > reliably (and force diagnostics when it is interpreted as a do-nothing):
    > ((a>b)?a:b) = 42;


    (a>b)?a:b = 42; means the same thing as ((a>b)?a:b) = 42

    (whatever it happens to mean).

    The parens would be necessar only if you need to port your code some poor
    quality compilers that cannot correctly parse the precedence of an assignment
    expression relative to a ternary operator.

    The extension is not syntactic but semantic.
     
    Kaz Kylheku, Jan 30, 2012
    #11
  12. James Kuyper <> writes:

    > [...] Note
    > that this is one of the differences between C and C++; the result of a
    > ?: expression is an lvalue in C++.


    I'd say "can be a lvalue" since it is not always an lvalue in C++. It's
    probably also worth pointing out that the syntax is different between C
    and C++. The original means (a > b ? a : b) = 42; in C, but it means
    a > b ? a : (b = 42); in C++.

    --
    Ben.
     
    Ben Bacarisse, Jan 30, 2012
    #12
  13. Anders Wegge Keller

    jacob navia Guest

    Le 30/01/12 16:32, Anders Wegge Keller a écrit :
    >
    > At work, we got into a talk about weird C constructs. One of my
    > collegues volunteered this line:
    >
    > (a>b)?a:b = 42;
    >


    lcc-win diagnostics this as an error. A ternary operator construct
    is not an lvalue.
     
    jacob navia, Jan 30, 2012
    #13
  14. Anders Wegge Keller

    Ben Pfaff Guest

    Anders Wegge Keller <> writes:

    > At work, we got into a talk about weird C constructs. One of my
    > collegues volunteered this line:
    >
    > (a>b)?a:b = 42;


    GCC 2.x and 3.x had an extension that allowed ?: to yield an
    lvalue:
    http://gcc.gnu.org/onlinedocs/gcc-3.0.4/gcc_5.html#SEC76
    It was never standard, rarely useful, and removed from GCC 4.x.
    --
    "Some people *are* arrogant, and others read the FAQ."
    --Chris Dollin
     
    Ben Pfaff, Jan 30, 2012
    #14
  15. Anders Wegge Keller

    BartC Guest

    "James Kuyper" <> wrote in message
    news:...
    > On 01/30/2012 11:43 AM, BartC wrote:
    >>
    >>
    >> "Devil with the China Blue Dress" <> wrote in message
    >> news:-september.org...
    >>> In article <>,
    >>> Anders Wegge Keller <> wrote:
    >>>
    >>>> At work, we got into a talk about weird C constructs. One of my
    >>>> collegues volunteered this line:
    >>>>
    >>>> (a>b)?a:b = 42;
    >>>
    >>> *(a>b ? &a : &b) = 42;

    >>
    >> There's no reason why the original version shouldn't work. ...

    >
    > That depends upon what you mean by "work". The original expression is
    > equivalent to
    >
    > b = 42;


    Only if the precedence of ?: is lower than for assignment. Used the other
    way:

    x = (a>b) ? a : b;

    You would expect the conditional expression to be evaluated first. Why not
    the same with the conditional on the left?

    --
    Bartc
     
    BartC, Jan 30, 2012
    #15
  16. gwowen <> writes:

    > On Jan 30, 3:32 pm, Anders Wegge Keller <> wrote:
    >
    > >  According to him, he have found three different compilers with three
    > > different ways of handling this. One did nothing, one always assigned
    > > to b, and the last one did what one should expect, were it legal.

    >
    > What should one expect?
    >
    > (a>b) ? a : (b=42);


    Worse than that:

    (a>b) ? (a=42) : (b=42);


    --
    /Wegge

    Leder efter redundant peering af dk.*,linux.debian.*
     
    Anders Wegge Keller, Jan 30, 2012
    #16
  17. Anders Wegge Keller

    BartC Guest

    "jacob navia" <> wrote in message
    news:jg6m3s$1n6$...
    > Le 30/01/12 16:32, Anders Wegge Keller a écrit :
    >>
    >> At work, we got into a talk about weird C constructs. One of my
    >> collegues volunteered this line:
    >>
    >> (a>b)?a:b = 42;
    >>

    >
    > lcc-win diagnostics this as an error. A ternary operator construct
    > is not an lvalue.


    Strangely, your compiler was the only one of four where this worked
    completely as expected!

    int a=400;
    int b=300;

    printf("A=%d B=%d\n",a,b);

    (a>b)?a:b = 42;

    printf("A'=%d B'=%d\n",a,b);


    --
    bartc
     
    BartC, Jan 30, 2012
    #17
  18. Kaz Kylheku <> writes:

    > On 2012-01-30, Anders Wegge Keller <> wrote:
    > >
    > > At work, we got into a talk about weird C constructs. One of my
    > > collegues volunteered this line:
    > >
    > > (a>b)?a:b = 42;


    ...

    >> We ended up discussing what the acutal problem here is.


    > Probably, that of not knowing that there exist different dialects of
    > C, and that your compiler might not be using the one you think it
    > is.


    No, actually we were discussing why this was prohibited under ISO C,
    and wondering exactly *what* part of the standard was violated.

    --
    /Wegge

    Leder efter redundant peering af dk.*,linux.debian.*
     
    Anders Wegge Keller, Jan 30, 2012
    #18
  19. Anders Wegge Keller

    James Kuyper Guest

    On 01/30/2012 01:16 PM, BartC wrote:
    >
    >
    > "James Kuyper" <> wrote in message
    > news:...
    >> On 01/30/2012 11:43 AM, BartC wrote:
    >>>
    >>>
    >>> "Devil with the China Blue Dress" <> wrote in message
    >>> news:-september.org...
    >>>> In article <>,
    >>>> Anders Wegge Keller <> wrote:
    >>>>
    >>>>> At work, we got into a talk about weird C constructs. One of my
    >>>>> collegues volunteered this line:
    >>>>>
    >>>>> (a>b)?a:b = 42;
    >>>>
    >>>> *(a>b ? &a : &b) = 42;
    >>>
    >>> There's no reason why the original version shouldn't work. ...

    >>
    >> That depends upon what you mean by "work". The original expression is
    >> equivalent to
    >>
    >> b = 42;

    >
    > Only if the precedence of ?: is lower than for assignment. Used the other
    > way:


    The ?: operator can't be explained in terms of a simple precedence
    relative to other operators.

    > x = (a>b) ? a : b;
    >
    > You would expect the conditional expression to be evaluated first. Why not
    > the same with the conditional on the left?


    Because I when gwowen wrote his comment, it sounded familiar, like
    something I already knew to be true, so I didn't bother to check. I just
    checked, and he was wrong, and I was therefore wrong to agree with his
    parse. The C grammar is:

    logical-OR-expression ? expression : conditional-expression

    In the following lines, I've inserted spaces to make corresponding parts
    line up with each other, but the alignment will not come out correctly
    unless viewed using a monospaced font:

    According to that grammar,
    a || b ? d , e : f ? g : h
    must be parsed as
    (a || b) ? (d , e) : (f ? g : h)
    but
    a ? b : c ? d , e : f = g
    must be parsed as
    (a ? b : (c ? (d , e) : f)) = g
    (that last parse results in a constraint violation, but it's not a
    syntax error).

    In all other contexts, the C grammar can be understood as giving || a
    higher precedence than =, giving both of them higher precedence than the
    comma operator. However, there's no way to insert ?: into that
    precedence hierarchy that explains both of the above parses.

    The C++ grammar is different, as I said, but I had the difference backwards:
    logical-or-expression ? expression : assignment-expression

    and that's probably what both gwowen and I were thinking about. In C++
    a || b ? d , e : f = g
    parses as:
    (a || b) ? (d , e) : (f = g)
    while
    a ? b : c ? d , e : f , g
    parses as:
    (a ? (b : c ? (d , e) : f)), g

    which is even harder to explain by inserting the ?: anywhere into the
    precedence hierarchy relative to ||, =, and the comma operator.
     
    James Kuyper, Jan 30, 2012
    #19
  20. Arbitrary assignment operation in any parenthesis allowed in C is just teasing
    programmers to write more debates for collecting taxes in books about C
    programming.

    Pascal and Fortran are better in this issue.
     
    88888 Dihedral, Jan 30, 2012
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Wenjie

    if (f() != FAIL) or if (FAIL != f())?

    Wenjie, Jul 28, 2003, in forum: C Programming
    Replies:
    3
    Views:
    452
    E. Robert Tisdale
    Jul 31, 2003
  2. Garg
    Replies:
    1
    Views:
    12,478
    Dejan Lazic
    Aug 4, 2006
  3. Replies:
    12
    Views:
    545
    Keith Thompson
    Mar 10, 2006
  4. Skybuck Flying
    Replies:
    62
    Views:
    2,025
    Rudy Velthuis
    Jun 29, 2010
  5. Gavin Kistner

    Too Many Ways?

    Gavin Kistner, Sep 30, 2004, in forum: Ruby
    Replies:
    97
    Views:
    820
    Mohammad Khan
    Oct 5, 2004
Loading...

Share This Page