Strange enum behavior with ternary operators - c

I was writing a very simple ternary operator with one of my enum and I stumbled upon what is a very strange error (to me..). Given this piece of code:
typedef enum
{
first = 0,
second,
last
}myEnum;
myEnum myVar = first;
(myVar < second) ? myVar++ : myVar = last;
The compiler sent me the following error:
error: lvalue required as left operand of assignment
Where as if I simply change the ternary to a if/else block like this :
if(myVar < second)
{
myVar++;
}
else
{
myVar = last;
}
Everything compiles and works fine. Can somebody explain why the exact same code written as a ternary won't compile? What am I missing?

Your expression is parsed as:
( (myVar < second) ? myVar++ : myVar ) = last;
but you seem to have intended to do:
(myVar < second) ? myVar++ : (myVar = last);
This is actually not exactly what the standard mandates (but many compilers parse it that way), the first expression should actually fail for another reason (a syntax error rather than a constraint violation).
C99 6.5.15 says:
conditional-expression:
logical-OR-expression
logical-OR-expression ? expression : conditional-expression
and myVar = last is not a conditional-expression but an assignment-expression (C99 6.5.16):
assignment-expression:
conditional-expression
unary-expression assignment-operator assignment-expression
but (myVar < second) ? myVar++ : myVar is not an unary-expression (see C99 6.5.3) (but the parenthesized version thereof would be, that is as I wrote in my first code snippet, see C99 6.5.1).
HTH

What you do and what you try to do is not comparable.
This one
if(myVar < second)
{
myVar++;
}
else
{
myVar = last;
}
translates to
myVar = (myvar < second) ? myvar + 1 : last;
If you want to completely build on side effects, you can as well do
(myVar < second) ? myVar++ : (myVar = last);
but I am not sure this is good style as it might confuse the reader about the intention of the expression.

Related

How is 0 used in conditional operator in C?

Conditional operator in C is used like this:
condition ? value_if_true : value_if_false
What does 0 mean when it's used in the value_if_false?
I've seen some people using it like this, for example.
a == b ? i++ : 0
It seems like it does nothing. Does this work like return 0 in other functions?
In C language, ternary is shorter version of if statement and it requires both statements, if_true and if_false. It would be like this (in fact it can have multiple statements for one case, separated with comma):
Short:
condition ? if_true : if_false;
Long:
if (condition) {
if_true;
} else {
if_false;
}
You can also assign the value if you put something infront of condition.
Short:
result = condition ? if_true : if_false;
Long:
if (condition) {
result = if_true;
} else {
result = if_false;
}
Now here is the trick. In C language, writing 0; is a valid statement, so your ternary becomes in longer version same as code below:
if (a == b) {
i++;
} else {
0; /* This is valid C statement */
}
Or if you have assignment too, it would be:
if (a == b) {
result = i++;
} else {
result = 0;
}
You can also do this:
int a;
/* Code here ... */
condition ? a = 5: 0;
That is effectively the same as:
if (condition) {
a = 5;
} else {
/* DO not touch a */
}
The ?: operator is a ternary operator, but it is not called "ternary" as some answers and/or comments here suggest. It just is the arity of the operator, just as + is a binary operator or as & is unary. If it has a name at all, it is called "Conditional Expression"-operator
It is not quite equivalent to if/else, because it is a conditional value (with the consequence, that both expressions must have the same type) in the first place, not a conditional execution. Of course, both types can be cast to make them equal.
In the case of what the OP does, a better option (if if shall not be used) is in my opinion:
a == b && i++;
which resembles a bit more logical what happens. But of course it is a matter of style.
The reason why someone might want to write a == b ? i++ : 0; is that s/he probably wants to have an (Caution! You are now entering an opinion-based area) easier and faster alternative to if (a == b) i++; - although this is of course opinion-based and I personally not share the same opinion.
One thing I can think of as a "blocker" at the if statement is the requirement to write the parentheses () which can be omitted by using the conditional operator instead.
"But why the 0?"
The C syntax requires a third operand for the conditional operator. Else if you would want to compile for example:
a == b ? i++;
you will get an error from the compiler:
"error: expected ':' before ';' token"
Or respectively, doing so:
a == b ? i++ : ;
would raise:
"error: expected expression before ';' token"
So they use 0 as kind of "syntax satisfier" to be able to use the conditional operator as replacement for the if statement. You could use any other numeral value here as well, but 0 is the most readable value, which signifies that it has no use otherwise.
To showcase the use at an example:
#include <stdio.h>
int main (void)
{
int a, b, c = 4;
a = 2;
b = 2;
a == b ? c++ : 0;
printf("%d",c);
return 0;
}
The output for c will be 5, because a == b.
Note that a == b ? i++ : 0 is different when used f.e. inside of an assignment like f.e.:
int c = a == b ? i++ : 0;
Here c is either getting assigned by i or 0, dependent upon a == b is true or not. If a == b is true, c is assigned by i. If a == b is wrong, c is assigned by 0.
Side Notes:
To view it from a technical point, ?= is called the "conditional operator". The conditional operator is one of the group of ternary operators.
If you want to learn more about the conditional operator ?=, look at ISO:IEC 9899:2018 (C18), §6.5.15 - "Conditional operator" for more information.
There's nothing special about 0 one could write
a == b ? i++ : 1
And it would behave the same way.
Only difference is when you assign the expression to say another variable:
int c = a == b ? i++ : 1;
// a == b -> c will be i++
// a != b -> c will be 1
However it's much cleaner to write
if (a == b) i++;
It helps to think of the ternary operator as a shorthand way or writing an if-else statement.
If(a == b){
i++;
}else{
//if assigned, return 0. Else do nothing.
}

(invalid lvalue in assignment) this error happens when i run it.what does it mean?

this is the code and the compiler says there is sth wrong with line 7.
include<stdio.h>
main()
{
char m;
int a,b,n=0;
scanf("%c%d%d",&m,&a,&b);
m=='A' || m=='B' || m=='C' ? n=(3*a)+(5*b) : n=(5*a)+(3*b);
printf("%d\n",n);
}
Use instead
m=='A' || m=='B' || m=='C' ? n=(3*a)+(5*b) : ( n=(5*a)+(3*b));
Otherwise the statement looks like
( m=='A' || m=='B' || m=='C' ? n=(3*a)+(5*b) : n)=(5*a)+(3*b);
Or you could write
n = m=='A' || m=='B' || m=='C' ? (3*a)+(5*b) : (5*a)+(3*b);
The conditional operator in C is defined the following way
conditional-expression:
logical-OR-expression
logical-OR-expression ? expression : conditional-expression
As the assignment operator has lower priority then the compiler issues an error because the assignment is excluded from the conditional operator for the third operand
The used by you expression would be valid in C++ because in C++ the operator is defined differently
conditional-expression:
logical-or-expression
logical-or-expression ? expression : assignment-expression
^^^^^^^^^^^^^^^^^^^^^
There is no need to use a complicated statement that confuses everyone, including the compiler. This is just as effective, and a lot easier to read:
if (m=='A' || m=='B' || m=='C')
n=(3*a)+(5*b);
else
n=(5*a)+(3*b);

Array initialization with a ternary operator?

I don't have access to the C11 specification, therefore I can't investigate this bug.
The following declaration rises an error during compilation:
int why[2] = 1 == 1 ? {1,2} : {3,4};
The error is: expected expression before { and: expected expression before :
This is not valid C11.
You can only initialize an array with an initializer-list not with an expression.
int why[2] = { ... }; // initializer-list {}
Moreover, 1 == 1 ? {1,2} : {3,4} is not a valid C expression because {1, 2} is not a C expression.
Just for information using compound literals you can have something close to what you want using a pointer object:
int *why = (1 == 1) ? (int[2]) {1,2} : (int[2]) {3,4};
from Charles Bailey's answer here: Gramma from conditional-expression
conditional-expression:
logical-OR-expression
logical-OR-expression ? expression : conditional-expression
And
1 == 1 ? {1,2} : {3,4};
^ ^ are not expressions
that is the reason compiler gives error like:
error: expected expression before ‘{’ token // means after ?
error: expected expression before ‘:’ token // before :
Edit as #Rudi Rüssel commented:
following is a valid code in c:
int main(){
{}
;
{1,2;}
}
we use {} to combine statements ; in C.
note: if I write {1,2} then its error (*expected ‘;’ before ‘}’ token*), because 1,2 is an expression but not a statement.
For OP: what is The Expression Statement in C and what is Block Statement and Expression Statements
edit2:
Note: How #ouah uses typecase to convert it into expression, yes:
To understand run this code:
int main(){
printf("\n Frist = %d, Second = %d\n",((int[2]){1,2})[0],((int[2]) {1,2})[1]);
}
It works like:
~$ ./a.out
Frist = 1, Second = 2
Initializer lists are not expressions, so they cannot be used in expressions.
I suggest you leave the array uninitialized and use memcpy.
int why[2];
memcpy( why, 1 == 1 ? (int[2]){1,2} : (int[2]){3,4}, sizeof why );

Precedence of assignment within a conditional operator

I've created this simple program to auto-generate sequence of frames to be used in Avisynth scipt:
#include <stdio.h>
int main(void) {
const int step = 3;
const int arr[] = {31997, 31998, 32001};
int i, ii = 0;
for(i = 32002; i <= 32121; i += step, (sizeof(arr)/sizeof(int) - 1 ) != ii ? ++ii : ii = 0) {
printf("freezeframe(%d,%d,%d)\n", i, i + step, arr[ii]);
}
return 0;
}
Using MinGW with GCC 4.6.2, I get this error: lvalue required as left operand of assignment.
The issue is simply solved by using parenthesis around ii=0. However, I don't get why it is an error. Shouldn't the assignment operator be evaluated first?
The conditional operator has higher precedence than the assignment operator in C.
(sizeof(arr)/sizeof(int) - 1 ) != ii ? ++ii : ii = 0
is evaluated as
((sizeof(arr)/sizeof(int) - 1 ) != ii ? ++ii : ii) = 0
For a quick reminder of the operators precedence in C, you can see this:
http://www.kernel.org/doc/man-pages/online/pages/man7/operator.7.html
Wikepedia has a short section which explains this: http://en.wikipedia.org/wiki/Operators_in_C_and_C++#Notes
The grammar for conditional operator in C is
logical-OR-expression ? expression : conditional-expression
Note that an assignment-expression is not considered a conditional-expression, and a conditional expression cannot be the left of an assignment expression and so it's technically a syntax error. See the grammar: http://www.lysator.liu.se/c/ANSI-C-grammar-y.html
However, GCC is (incorrectly) parsing it as:
((sizeof(arr)/sizeof(int) - 1 ) != ii ? ++ii : ii) = 0
Which is a semantic error since ++i is not an lvalue expression.
It is always advised to use parenthesis, if you write your code in this fashion
(sizeof(arr)/sizeof(int) - 1 ) != ii ? ++ii : ii = 0
like
(((sizeof(arr)/sizeof(int) - 1 ) != ii ? ++ii : ii) = 0)
9 out of 10 times you will stuck.
So make a habit of putting parenthesis and this doesn't make any overhead on the compilers!!!

goto not working with ?: operator in C

For learning purposes, I wrote the following code snippet:
for(int i=0;i<10;i++)
{
for(int j = 0;j<5;j++)
{
//(i==j && i==3)? (goto found) : printf("stya here\n");
if(i==j && i==3){goto found;} else {printf("stay here\n");}
}
}
found:
printf("yes I am here");
But I wondered when I discovered the omitted statement inside the inner loop not gives error and now I am confused about if-else is not always replaceable with ?: operator. What is the fact here? Why does the commented statement give an error?
The ?: operator is not replacement for if. It works only for expressions: condition ? expr1 : expr2 where both sub-expressions expr1 and expr2 are of the same type (and the whole expression then is of the same type).
goto is not expression, it is a statement.
I am not well versed enough in C to explain why this doesn't work syntactically, but in the sense of intent the ?: ternary operator form is intended as a conditional expression (yields a result), not as a control flow mechanism. Using the if statement you can choose a value for a variable or change the flow of the application.
e.g.
//Change flow
if(x ==0)
{
//do this
}
else
{
//goto some label
}
or
//Change value
if(x == 0)
{
y = 1;
}
else
{
y = 2;
}
The ternary is only intended for the second case, as a conditional expression
i.e.
y = (x ==0) ? 1 :2;
Actually, what you're trying to do with goto and the ternary operator is possible if your compiler support the extension Statement Expressions, as it's name said, this extension allow you to write statements inside an expression or sub-expressions, just like this:
(rand() % 2) ? ({goto lbl1;}) : ({goto lbl2;});
Using these statements can be very useful (mostly to optimize macros) but often lead to very dirty code, just like the example i gave :)
So to complement the other answers i'll say that it's not possible in C99/11 without extension but most of the recent compiler support a bunch of extension that allows you to do non-standard cool things.
What would be the result of "goto found" expression? I don't know, neither does the compiler, so the result of ? expression cannot be determined, hence the error.
In general, the ?: operator is no replacement for a classic if() ... else() .... It might be used as such if both operators (and the condition) are values or expressions returning a value. You can't use them with statements like goto, break or continue.
The following would be possible:
condition ? dothis() : dothat(); // there's no assignment, but it's still valid
var = condition ? dothis() : othervar;
condition ? (var=4, othervar=3) : (somevar = 1);
But you can't include anything that's not an expression (i.e. nothing not having some value or result):
condition ? continue : break; // statements letting the execution continue somewhere else
condition ? {var = 4; othervar = 3;} : dothat(); // trying to inline scopes/multiple exressions
var = condition ? while(var) {var--;} : 5; // similar, inlining a complete loop
These last examples can be done, but they'd require you to use if() or function bodys to call:
if (condition) continue; else break;
condition ? (var = 4, var = 3) : dothat();
var = condition ? dotheloop(var) : 5; // ok, this could be 'var = condition ? 0 : 5;' but... example code

Resources