?: as an lvalue

L

lawrence.jones

Harald van D??k said:
Allowing ?: to be used as an lvalue could break code, by the way:
currently,

volatile int i;
volatile int j;
rand() ? i : j;

causes a read of both i and j, while this would change to read only one
of them.

Huh? The definition of ?: currently says that only one of the second
and third operands are evaluated, so either i or j is evaluated but not
both, so only one is read.

-Larry Jones

I'm getting disillusioned with these New Years. -- Calvin
 
B

Ben Bacarisse

Flash Gordon said:
Philip Potter wrote, On 01/04/08 13:15:
Bartc said:
Flash Gordon wrote:
Bartc wrote, On 31/03/08 18:45:
[discussion of ?: as an lvalue compared to other lvalue expressions]
a: You can write a = x without writing *(&a) = x
You can't do 3 = x

a.b: You can write a.b = x without writing *(&a + offsetof b) = x
You can't do 5.b or a.5
But you can do f().b
E1.E2 is only an lvalue if E1 is an lvalue.

Well f().b = x would be an error too. I was pointing out that where
a.b=x is legal, you can write it without all this *(&a+offsetof
business, because compiler magic is being applied.

That's exactly the point I was making too. I was saying that a.b is
conditionally an lvalue based on a,

Well, since that is a post C99 change to the language I'm sure you can
forgive some people for forgetting it.

There is almost identical wording in my text-only version of C90. I
don't think the idea is a new one.
 
H

Harald van Dijk

Huh? The definition of ?: currently says that only one of the second
and third operands are evaluated, so either i or j is evaluated but not
both, so only one is read.

Oh dear. Firstly, I meant that it would change to read none instead of
one, and secondly, I was wrong about that anyway, because I was thinking
of C++ rules where lvalue to value conversion does not happen in
expression statements.
 
A

Andrey Tarasevich

Ben said:
Flash Gordon said:
Philip Potter wrote, On 01/04/08 13:15:
Bartc wrote:
Flash Gordon wrote:
Bartc wrote, On 31/03/08 18:45:
[discussion of ?: as an lvalue compared to other lvalue expressions]
a: You can write a = x without writing *(&a) = x
You can't do 3 = x

a.b: You can write a.b = x without writing *(&a + offsetof b) = x
You can't do 5.b or a.5
But you can do f().b
E1.E2 is only an lvalue if E1 is an lvalue.
Well f().b = x would be an error too. I was pointing out that where
a.b=x is legal, you can write it without all this *(&a+offsetof
business, because compiler magic is being applied.
That's exactly the point I was making too. I was saying that a.b is
conditionally an lvalue based on a,
Well, since that is a post C99 change to the language I'm sure you can
forgive some people for forgetting it.

There is almost identical wording in my text-only version of C90. I
don't think the idea is a new one.

There's no difference between C89/90 and C99 in this regard.

The only semi-relevant difference there one can find is related to the
handling of arrays. In C89/90 it was illegal to do this

struct S { int a[10]; } foo(void);
...
foo().a[1]; // error in C89/90

because in C89/90 array-to-pointer conversion (essential to the
semantics of '[]') was not allowed for non-lvalue arrays.

In C99 array-to-pointer conversion is applied to non-lvalue arrays,
meaning that the above code becomes legal. An interesting side effect of
this change is that in C99 the following is legal as well

foo().a[1] = 5;
 
H

Harald van Dijk

struct S { int a[10]; } foo(void);
...
foo().a[1]; // error in C89/90
[...]
In C99 array-to-pointer conversion is applied to non-lvalue arrays,
meaning that the above code becomes legal. An interesting side effect of
this change is that in C99 the following is legal as well

foo().a[1] = 5;

In C99, this violates no constraints, but if it is executed, the
behaviour is undefined. And unfortunately, even if you do what you can to
get a notice from compilers:

const struct S { int a[10]; } foo(void);
foo().a[1] = 5;

still no constraints are violated.
 
F

Flash Gordon

Ben Bacarisse wrote, On 02/04/08 00:40:
Flash Gordon said:
Philip Potter wrote, On 01/04/08 13:15:
Bartc wrote:
Flash Gordon wrote:
Bartc wrote, On 31/03/08 18:45:
[discussion of ?: as an lvalue compared to other lvalue expressions]
a: You can write a = x without writing *(&a) = x
You can't do 3 = x

a.b: You can write a.b = x without writing *(&a + offsetof b) = x
You can't do 5.b or a.5
But you can do f().b
E1.E2 is only an lvalue if E1 is an lvalue.
Well f().b = x would be an error too. I was pointing out that where
a.b=x is legal, you can write it without all this *(&a+offsetof
business, because compiler magic is being applied.
That's exactly the point I was making too. I was saying that a.b is
conditionally an lvalue based on a,
Well, since that is a post C99 change to the language I'm sure you can
forgive some people for forgetting it.

There is almost identical wording in my text-only version of C90. I
don't think the idea is a new one.

Ah well, put it down to the fact I've never considered using the .
operator without the left operand being an lvalue.
 
L

lawrence.jones

Andrey Tarasevich said:
In C99 array-to-pointer conversion is applied to non-lvalue arrays,
meaning that the above code becomes legal. An interesting side effect of
this change is that in C99 the following is legal as well

foo().a[1] = 5;

No, it's not:

If an attempt is made to modify the result of a function call or
to access it after the next sequence point, the behavior is
undefined.

-Larry Jones

My brain is trying to kill me. -- Calvin
 
K

Keith Thompson

Andrey Tarasevich said:
In C99 array-to-pointer conversion is applied to non-lvalue arrays,
meaning that the above code becomes legal. An interesting side effect of
this change is that in C99 the following is legal as well

foo().a[1] = 5;

No, it's not:

If an attempt is made to modify the result of a function call or
to access it after the next sequence point, the behavior is
undefined.

Undefined behavior is illegal?
 
L

lawrence.jones

Keith Thompson said:
Undefined behavior is illegal?

Well, the C Police aren't going to come and arrest you, but it's not
valid, which is what most people mean by "legal".

-Larry Jones

Hmm... That might not be politic. -- Calvin
 
H

Harald van Dijk

Well, the C Police aren't going to come and arrest you, but it's not
valid, which is what most people mean by "legal".

Most people don't consider the things illegal that the police turn a
blind eye towards. Undefined behaviour is what happens when your program
doesn't play by C's rules, but at the same time, C's rules tell your
compiler not to arrest you.
 
W

Wolfgang Draxinger

Aside the suggestion already made

*(a ? &b : &c) = d;

I'd like to know if this would be as valid, or to be more
precisely, does the conditional operator guarantee, that only
the selected expression is actually evaluated.

a ? (b=d) : (c=d);

I'm too lazy to look in the standard now, but common sense tells
me, that only evaluation of the selected expression must take
place, since functions might be called, that change the global
state of the program. And in case of pointer:

int *p; /*something_usefull or NULL*/
int a;
p != NULL ? a = *p : a = -1;

Any preconditional evaluation might end up in a segmentation
violation.

Wolfgang Draxinger
 
B

Ben Pfaff

Wolfgang Draxinger said:
I'd like to know if this would be as valid, or to be more
precisely, does the conditional operator guarantee, that only
the selected expression is actually evaluated.

a ? (b=d) : (c=d);

Yes and yes.
 
R

Richard Tobin

Wolfgang Draxinger said:
*(a ? &b : &c) = d;

I don't recommend this. It hides the intention with a use of pointers
that would otherwise never exist.
I'd like to know if this would be as valid, or to be more
precisely, does the conditional operator guarantee, that only
the selected expression is actually evaluated.

a ? (b=d) : (c=d);

This does indeed only evaluate the selected assignment, but removes
the most important motivation for using ?: instead of an if statement,
viz to indicate that the choice is only between the assignment
targets. Unless you *need* an expression (for example in a macro), I
recommend using the simple if statement instead, even at the expense
of a fair amount of vertical whitespace. After all, it's hardly a
common situation, to be choosing between the target rather than the
values.

-- Richard
 
K

Keith Thompson

Harald van Dijk said:
Most people don't consider the things illegal that the police turn a
blind eye towards. Undefined behaviour is what happens when your program
doesn't play by C's rules, but at the same time, C's rules tell your
compiler not to arrest you.

Actually, C's rules specifically *don't* tell the compiler not to
arrest you. Printing an error message and terminating, either at
compile time or at run time, is well within the (nonexistent) bounds
of undefined behavior.

Personally, I tend to think of anything that requires a diagnostic (a
syntax error or constraint violation) as "illegal" -- and even then it
can be "legal" in the sense that the implementation is still allowed
to produce an executable.

If everybody else thinks of UB as "illegal", I suppose I'll go along
with the consensus, but I don't think I'm the only one who finds the
usage ambiguous.
 
A

Andrey Tarasevich

Andrey Tarasevich said:
In C99 array-to-pointer conversion is applied to non-lvalue arrays,
meaning that the above code becomes legal. An interesting side effect of
this change is that in C99 the following is legal as well

foo().a[1] = 5;

No, it's not:

If an attempt is made to modify the result of a function call or
to access it after the next sequence point, the behavior is
undefined.

I was talking about the syntactical legality, meaning that the above
code doesn't violate any syntax rules or constraints of C99, as opposed
to those of C89/90. This is, I believe, what the word "legal" usually
means in the context of C language (due to lack of more tightly defined
terms like ill-/well-formed in C++).
 
H

Harald van Dijk

Actually, C's rules specifically *don't* tell the compiler not to arrest
you. Printing an error message and terminating, either at compile time
or at run time, is well within the (nonexistent) bounds of undefined
behavior.

I stand by my previous statement.

void f(){}
void g(){if(0)f(1);}

This has been declared a valid translation unit, and it may appear in
strictly conforming programs. A compiler is not allowed to reject it, as
long as the call to f is never executed, and in the general case, it's
not possible for the compiler to determine whether such a call would ever
be executed.
 
K

Keith Thompson

Harald van Dijk said:
I stand by my previous statement.

void f(){}
void g(){if(0)f(1);}

This has been declared a valid translation unit, and it may appear in
strictly conforming programs. A compiler is not allowed to reject it, as
long as the call to f is never executed, and in the general case, it's
not possible for the compiler to determine whether such a call would ever
be executed.

Right. Since the call f(1) never occurs, there's no undefined
behavior.

Perhaps I should have been clearer about the fact that UB can cause a
translation unit to be rejected by the compiler only if it can prove
that the UB will always happen, but I didn't think it was an important
point in context.

I'm not aware of any point on which we disagree.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,432
Messages
2,571,680
Members
48,796
Latest member
Greg L.

Latest Threads

Top