I was writing a console application that would try to "guess" a number by trial and error, it worked fine and all but it left me wondering about a certain part that I wrote absentmindedly,
The code is:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int x,i,a,cc;
for(;;){
scanf("%d",&x);
a=50;
i=100/a;
for(cc=0;;cc++)
{
if(x<a)
{
printf("%d was too big\n",a);
a=a-((100/(i<<=1))?:1);
}
else if (x>a)
{
printf("%d was too small\n",a);
a=a+((100/(i<<=1))?:1);
}
else
{
printf("%d was the right number\n-----------------%d---------------------\n",a,cc);
break;
}
}
}
return 0;
}
More specifically the part that confused me is
a=a+((100/(i<<=1))?:1);
//Code, code
a=a-((100/(i<<=1))?:1);
I used ((100/(i<<=1))?:1) to make sure that if 100/(i<<=1) returned 0 (or false) the whole expression would evaluate to 1 ((100/(i<<=1))?:***1***), and I left the part of the conditional that would work if it was true empty ((100/(i<<=1))? _this space_ :1), it seems to work correctly but is there any risk in leaving that part of the conditional empty?
This is a GNU C extension (see ?: wikipedia entry), so for portability you should explicitly state the second operand.
In the 'true' case, it is returning the result of the conditional.
The following statements are almost equivalent:
a = x ?: y;
a = x ? x : y;
The only difference is in the first statement, x is always evaluated once, whereas in the second, x will be evaluated twice if it is true. So the only difference is when evaluating x has side effects.
Either way, I'd consider this a subtle use of the syntax... and if you have any empathy for those maintaining your code, you should explicitly state the operand. :)
On the other hand, it's a nice little trick for a common use case.
This is a GCC extension to the C language. When nothing appears between ?:, then the value of the comparison is used in the true case.
The middle operand in a conditional expression may be omitted. Then if the first operand is nonzero, its value is the value of the conditional expression.
Therefore, the expression
x ? : y
has the value of x if that is nonzero; otherwise, the value of y.
This example is perfectly equivalent to
x ? x : y
In this simple case, the ability to omit the middle operand is not especially useful. When it becomes useful is when the first operand does, or may (if it is a macro argument), contain a side effect. Then repeating the operand in the middle would perform the side effect twice. Omitting the middle operand uses the value already computed without the undesirable effects of recomputing it.
Related
I was messing around with some c++ code after learning that you cannot increment booleans in the standard. I thought incrementing booleans would be useful because I like compacting functions if I can and
unsigned char b = 0; //type doesn't really matter assuming no overflow
while (...) {
if (b++) //do something
...}
is sometimes useful to have, but it would be nice to not have to worry about integer overflow. Since booleans cannot take on any value besides 0 or 1, I thought that perhaps this would work. Of course, you could just have a boolean assigned to 1 after you do your operation but that takes an extra line of code.
Anyway, I thought about how I might achieve this a different way and I came up with this.
bool b = 0;
while (...)
if (b & (b=1)) ...
This turned out to always evaluate to true, even on the first pass through. I thought - sure, its just doing the bitwise operator from right to left, except that when I swapped the order, it did the same thing.
So my question is, how is this expression being evaluated so that it simplifies to always being true?
As an aside, the way to do what I wanted is like this I guess:
bool b = 0;
while (...) if (!b && !(b=1)) //code executes every time except the first iteration
There's a way to get it to only execute once, also.
Or just do the actually readable and obvious solution.
bool b = 0;
while (...) {
if (b) {} else {b=1;}
}
This is what std::exchange is for:
if (std::exchange(b, true)) {
// b was already true
}
As mentioned, sequencing rules mean that b & (b=1) is undefined behaviour in both C and C++. You can't modify a variable and read from it "without proper separation" (e.g., as separate statements).
There is no "strange behavior" here. The & operator, in your case is a bitwise arithmetic operator. These operator are commutative, so both operand must be evaluated before computing the resulting value.
As mentioned earlier, this is considered as undefined behavior, as the standard does not prevent computing + loading into a register the left operand, before doing the same to the second, or doing so in reverse order.
If you are using C++14 or above, you could use std::exchange do achieve this, or re-implement it quite easily using the following :
bool exchange_bool(bool &var, bool &&val) {
bool ret = var;
var = val;
return ret;
}
If you end up re-implementing it, consider using template instead of hard-coded types.
As a proper answer to your question :
So my question is, how is this expression being evaluated so that it simplifies to always being true?
It does not simplify, if we strictly follow the standard, but I'd guess you are always using the same compiler, which end up producing the same output.
Side point: You should not "compact" functions. Most of the time the price of this is less readability. This might be fine if you're the only dev working on a project, but on bigger project, or if you come back to your code later, that could be an issue. Last but not least, "compact" function are more error prone, because of their lack of readability.
If you really want shorter function, you should consider using sub-function that will handle part of the function computation. This would enable you to write short function, to quickly fix or update behavior, without impacting clarity.
I don't see what meaning you can assign to a "bitwise increment" operator. If I am right, what you achieve is just setting the low-order bit, which is done by b|= 1 (following the C conventions, this could have been defined as a unary b||, but the || was assigned another purpose). But I don't feel that this is a useful bitwise operation, and it can increment only once. IMO the standard increment operator is more bitwise.
If your goal was in fact to "increment" a boolean variable, i.e. set it to true (prefix or postfix), b= b || true is a way. (There is no "logical OR assignement" b||= true, and even less a "logical increment" b||||.)
I've been familiar with the ternary operator for quite some time now, and have worked with it in a few differnet languages. My understanding of the operator is this:
condition ? expr1 : expr2
However, in C++, the following code is legal:
int i = 45;
(i > 0) ? i-- : 1;
Aren't you, in effect, just writing 1; or i - 1;How is this a complete statement? I understand that the intention of the code is to decrement i if it's greater than 0, but I would've thought that the code would generate a compiler error as just being an expression, not a full statement. I expected code like this:
int i = 45;
i = (i > 0) ? i - 1 : i;
This is called expression statement. The expression is evaluated and its value is discarded.
Even this is valid:
42;
although it does nothing. Only side effects (like i--, assignment, etc) in the expression have effects.
In fact, many statements we use are expression statements: assignments, function calls, etc:
a = 42;
foo();
That is a valid expression. You might have received a warning because you are not saving the result of the expression, but that you have the i-- your statement does have an effect.
In C++, an expression like 1 is a perfectly valid statement with no side effects. You could very feasibly write this function:
void f() {
1;
}
In fact, even this is correct.
void f() {
;;;;
}
A literal statement evaluates its arguments but does nothing more. The system views 1; as being just like func();. The only difference is that while func(); would logically have some side effects, 1; does not so it ends up being a no-op. The ternary operator evaluates like an if-statement, so the second form is only evaluated if the operand is true. Thus:
(i > 0) ? i-- : 1;
If i is greater than 0, the second form is evaluated. When it is evaluated, it carries its side effect, which decrements i by 1. Otherwise, the third form is evaluated, which does nothing. Although this block of code works, it is not incredibly readable, so while it's nice toy code a real if-statement is ideal for situations like this. For the same reason, this line would have the same effect but be frowned upon for being equally unreadable.
((i > 0) && (i--)) || 1;
Assuming you didn't overwrite the boolean operators, this code will short-circuit and behave like the ternary operator. If i is not greater than 0, then the && need not evaluate its second operand since the && is false, but the || must since it might be true. Inversely, if i is greater than 0, the && needs to evaluate but the || already knows it's true.
Aren't you, in effect, just writing 1; or i - 1;
No: i-- is not the same as i - 1. In the first case, the value of i is modified. In the second case it is not.
In the event that i less than or equal to zero, then you're correct that the resulting 'code' will be 1. However, the compiler will realise that this is not a useful thing to execute and so it ought to generate code equivalent to:
if( i > 0 ) i--;
Some (including myself) would consider that using the ternary operator in this fashion is bad style. Just for fun, here's another way someone might write it that's also not very nice (also more likely to generate compiler warning):
i > 0 && i--;
In the end, style is a matter of preference. The compiler, for the most part, will decide the best way to turn your code into assembly. So you owe it to yourself to write code that is clear and concise.
Is it possible to do the following without getting a stackoverflow?
int foo(int i = 0)
{
return foo(--i) || i;
}
When foo(--i) evaluates to 0, it should return false, therefore returning i and ending the execution.
You want return !i || foo(--i).
Note that || is short-circutted. This means that evaluation (which is performed from left to right) will continue for only as long as the result of the expression is not known. So the way I've written it, the case i being zero will block the recursion.
(Moving on to more advanced issues, you need to be very careful when evaluating expressions when the same variable appears in more than one sub-expression and it's value is changed in some of them. My having !i and --i could get me in trouble: I'm not too far away from undefined behaviour here. In fact, it turns out that my code is completely safe since || is a sequence point and the order of evaluation with || is well-defined. But do be careful.)
From what you have coded foo never returns because it will continuosly go on decreasing i and recurse. You need a check inside foo which returns whatever you want when you get i as 0.
I guess the answer is "no", but from a compiler point of view, I don't understand why.
I made a very simple code which freaks out compiler diagnostics quite badly (both clang and gcc), but I would like to have confirmation that the code is not ill formatted before I report mis-diagnostics. I should point out that these are not compiler bugs, the output is correct in all cases, but I have doubts about the warnings.
Consider the following code:
#include <iostream>
int main(){
int b,a;
b = 3;
b == 3 ? a = 1 : b = 2;
b == 2 ? a = 2 : b = 1;
a = a;
std::cerr << a << std::endl;
}
The assignment of a is a tautology, meaning that a will be initialized after the two ternary statements, regardless of b. GCC is perfectly happy with this code. Clang is slighly more clever and spot something silly (warning: explicitly assigning a variable of type 'int' to itself [-Wself-assign]), but no big deal.
Now the same thing (semantically at least), but shorter syntax:
#include <iostream>
int main(){
int b,a = (b=3,
b == 3 ? a = 1 : b = 2,
b == 2 ? a = 2 : b = 1,
a);
std::cerr << a << std::endl;
}
Now the compilers give me completely different warnings. Clang doesn't report anything strange anymore (which is probably correct because of the parenthesis precedence). gcc is a bit more scary and says:
test.cpp: In function ‘int main()’:
test.cpp:7:15: warning: operation on ‘a’ may be undefined [-Wsequence-point]
But is that true? That sequence-point warning gives me a hint that coma separated statements are not handled in the same way in practice, but I don't know if they should or not.
And it gets weirder, changing the code to:
#include <iostream>
int main(){
int b,a = (b=3,
b == 3 ? a = 1 : b = 2,
b == 2 ? a = 2 : b = 1,
a+0); // <- i just changed this line
std::cerr << a << std::endl;
}
and then suddenly clang realized that there might be something fishy with a:
test.cpp:7:14: warning: variable 'a' is uninitialized when used within its own initialization [-Wuninitialized]
a+0);
^
But there was no problem with a before... For some reasons clang cannot spot the tautology in this case. Again, it might simply be because those are not full statements anymore.
The problems are:
is this code valid and well defined (in all versions)?
how is the list of comma separated statements handled? Should it be different from the first version of the code with explicit statements?
is GCC right to report undefined behavior and sequence point issues? (in this case clang is missing some important diagnostics) I am aware that it says may, but still...
is clang right to report that a might be uninitialized in the last case? (then it should have the same diagnostic for the previous case)
Edit and comments:
I am getting several (rightful) comments that this code is anything but simple. This is true, but the point is that the compilers mis-diagnose when they encounter comma-separated statements in initializers. This is a bad thing. I made my code more complete to avoid the "have you tried this syntax..." comments. A much more realistic and human readable version of the problem could be written, which would exhibit wrong diagnostics, but I think this version shows more information and is more complete.
in a compiler-torture test suite, this would be considered very understandable and readable, they do much much worse :) We need code like that to test and assess compilers. This would not look pretty in production code, but that is not the point here.
5 Expressions
10 In some contexts, an expression only appears for its side effects. Such an expression is called a discarded-value
expression. The expression is evaluated and its value is discarded
5.18 Comma operator [expr.comma]
A pair of expressions separated by a comma is evaluated left-to-right;
the left expression is a discarded-value expression (Clause 5).83 Every
value computation and side effect associated with the left expression
is sequenced before every value computation and side effect associated
with the right expression. The type and value of the result are the
type and value of the right operand; the result is of the same value
category as its right operand, and is a bit-field if its right operand
is a glvalue and a bit-field.
It sounds to me like there's nothing wrong with your statement.
Looking more closely at the g++ warning, may be undefined, which tells me that the parser isn't smart enough to see that a=1 is guaranteed to be evaluated.
I've read that usually statements in c++ end with a semi-colon; so that might help explain what an expression statement would be. But then what would you call an expression by giving an example?
In this case, are both just statements or expression statements or expressions?
int x;
x = 0;
An expression is "a sequence of operators and operands that specifies a computation" (that's the definition given in the C++ standard). Examples are 42, 2 + 2, "hello, world", and func("argument"). Assignments are expressions in C++; so are function calls.
I don't see a definition for the term "statement", but basically it's a chunk of code that performs some action. Examples are compound statements (consisting of zero or more other statements included in { ... }), if statements, goto statements, return statements, and expression statements. (In C++, but not in C, declarations are classified as statements.)
The terms statement and expression are defined very precisely by the language grammar.
An expression statement is a particular kind of statement. It consists of an optional expression followed by a semicolon. The expression is evaluated and any result is discarded. Usually this is used when the statement has side effects (otherwise there's not much point), but you can have a expression statement where the expression has no side effects. Examples are:
x = 42; // the expression happens to be an assignment
func("argument");
42; // no side effects, allowed but not useful
; // a null statement
The null statement is a special case. (I'm not sure why it's treated that way; in my opinion it would make more sense for it to be a disinct kind of statement. But that's the way the standard defines it.)
Note that
return 42;
is a statement, but it's not an expression statement. It contains an expression, but the expression (plus the ;) doesn't make up the entire statement.
These are expressions (remember math?):
1
6 * 7
a + b * 3
sin(3) + 7
a > b
a ? 1 : 0
func()
mystring + gimmeAString() + std::string("\n")
The following are all statements:
int x; // Also a declaration.
x = 0; // Also an assignment.
if(expr) { /*...*/ } // This is why it's called an "if-statement".
for(expr; expr; expr) { /*...*/ } // For-loop.
A statement is usually made up of an expression:
if(a > b) // a > b is an expr.
while(true) // true is an expr.
func(); // func() is an expr.
To understand what is an expression statement, you should first know what is an expression and what is an statement.
An expression in a programming language is a combination of one or more explicit values, constants, variables, operators, and functions that the programming language interprets (according to its particular rules of precedence and of association) and computes to produce ("to return", in a stateful environment) another value. This process, as for mathematical expressions, is called evaluation.
Source: https://en.wikipedia.org/wiki/Expression_(computer_science)
In other words expressions are a sort of data items. They can have single or multiple entities like constants and variables. These entities may be related or connected to each other by operators. Expressions may or may not have side effects, in that they evaluate to something by means of computation which changes a state. For instance numbers, things that look like mathematical formulas and calculations, assignments, function calls, logical evaluations, strings and string operations are all considered expressions.
function calls: According to MSDN, function calls are considered expressions. A function call is an expression that passes control and arguments (if any) to a function and has the form:
expression (expression-list opt) which is invoked by the ( ) function operator.
source: https://msdn.microsoft.com/en-us/library/be6ftfba.aspx
Some examples of expressions are:
46
18 * 3 + 22 / 2
a = 4
b = a + 3
c = b * -2
abs(c)
b >= c
c
"a string"
str = "some string"
strcat(str, " some thing else")
str2 = "some string" + " some other string" // in C++11 using string library
Statements are fragments of a program that execute in sequence and cause the computer to carry out some definite action. Some C++ statement types are:
expression statements;
compound statements;
selection statements;
iteration statements;
jump statements;
declaration statements;
try blocks;
atomic and synchronized blocks (TM TS).
Source: http://en.cppreference.com/w/cpp/language/statements
I've read usually statements in c++ ends with a semicon;
Yes usually! But not always. Consider the following piece of code which is a compound statement but does not end with a semicolon, rather it is enclosed between two curly braces:
{ // begining of a compound statement
int x; // A declaration statement
int y;
int z;
x = 2; // x = 2 is an expression, thus x = 2; with the trailing semicolon is an expression statement
y = 2 * x + 5;
if(y == 9) { // A control statement
z = 52;
} else { // A branching statement of a control statement
z = 0;
}
} // end of a compound statement
By now, as you might be guessing, an expression statement is any statement that has an expression followed by a semicolon. According to MSDN an expression statement is a statement that causes the expressions to be evaluated. No transfer of control or iteration takes place as a result of an expression statement.
Source: https://msdn.microsoft.com/en-us/library/s7ytfs2k.aspx
Some Examples of expression statements:
x = 4;
y = x * x + 10;
radius = 5;
pi = 3.141593;
circumference = 2. * pi * radius;
area = pi * radius * radius;
Therefore the following can not be considered expression statements since they transfer the control flow to another part of a program by calling a function:
printf("The control is passed to the printf function");
y = pow(x, 2);
side effects: A side effect refers to the modification of a state. Such as changing the value of a variable, writing some data on a disk showing a menu in the User Interface, etc.
Source: https://en.wikipedia.org/wiki/Side_effect_(computer_science)
Note that expression statements don't need to have side effects. That is they don't have to change or modify any state. For example if we consider a program's control flow as a state which could be modified, then the following expression statements
won't have any side effects over the program's control flow:
a = 8;
b = 10 + a;
k++;
Wheres the following expression statement would have a side effect, since it would pass the control flow to sqrt() function, thus changing a state:
d = sqrt(a); // The control flow is passed to sqrt() function
If we consider the value of a variable as a state as well, modifying it would be a side effect thus all of expression statements above have side effects, because they all modify a state. An expression statement that does not have any side effect is not very useful. Consider the following expression statements:
x = 7; // This expression statement sets the value of x to 7
x; // This expression statement is evaluated to 7 and does nothing useful
In the above example x = 7; is a useful expression statement for us. It sets the value of x to 7 by = the assignment operator. But x; evaluates to 7 and it doesn't do anything useful.
According to The C++ Programming Language by Bjarne Stroustrup Special(3rd) Edition, a statement is basically any declaration, function call, assignment, or conditional. Though, if you look at the grammar, it is much more complicated than that. An expression, in simple terms, is any math or logical operation(s).
The wikipedia links that ok posted in his answer can be of help too.
In my opinion,
a statement *states* the purpose of a code block. i.e. we say this block of code if(){} is an if-statement, or this x=42; is an expression statement. So code such as 42; serves no purporse, therefore, this is *not* a statement.
and,
an expression is any legal combination of symbols that represents a value (Credit to Webopedia); it combines variables and constants to produce new values(Quoted from Chapter 2 in The C Programming Language). Therefore, it also has a mathematical connotation. For instance, number 42 in x=42; is an expression (x=42; is not an expression but rather an expression statement), or func(x) is an expression because it will evaluate to something. On the contrary, int x; is not an expression because it is not representing any value.
I think this excerpt from a technical book is most useful and clear.
Read the paragraphs till the start of 1.4.2 statements would be useful enough.
An expression is "a sequence of operators and operands that specifies a computation"
These are expressions:
1
2 + 2
"hi"
cout << "Hello, World!"
The last one is indeed an expression; << is the output operator, cout (of type ostream) and "Hello, World!" (string literals) are the operands. The operator returns the left-hand operand, so (cout << "Hello, ") << "World!" is also a valid expression but also not a statement.
An expression becomes an expression statement when it is followed by a semicolon:
1;
2 + 2;
"hi";
cout << "Hello, World!";
An expression is part of a statement, OR a statement itself.
int x; is a statement and expression.
See this : http://en.wikipedia.org/wiki/Expression_%28programming%29
http://en.wikipedia.org/wiki/Statement_%28programming%29