Mentions légales du service

Skip to content
Snippets Groups Projects

Precise logical operators evaluation order

1 file
+ 2
0
Compare changes
  • Side-by-side
  • Inline
%% Cell type:markdown id: tags:
# [Getting started in C++](./) - [Procedural programming](./0-main.ipynb) - [Conditions and loops](./2-Conditions-and-loops.ipynb)
%% Cell type:markdown id: tags:
<h1>Table of contents<span class="tocSkip"></span></h1>
<div class="toc"><ul class="toc-item"><li><span><a href="#Conditions" data-toc-modified-id="Conditions-1">Conditions</a></span><ul class="toc-item"><li><span><a href="#if-condition-followed-by-a-single-statement" data-toc-modified-id="if-condition-followed-by-a-single-statement-1.1"><code>if</code> condition followed by a single statement</a></span></li><li><span><a href="#if-condition-followed-by-a-block" data-toc-modified-id="if-condition-followed-by-a-block-1.2"><code>if</code> condition followed by a block</a></span></li><li><span><a href="#No-semicolon-at-the-end-of-the-if-condition" data-toc-modified-id="No-semicolon-at-the-end-of-the-if-condition-1.3">No semicolon at the end of the <code>if</code> condition</a></span></li><li><span><a href="#if-...-else-if-...-else" data-toc-modified-id="if-...-else-if-...-else-1.4">if ... else if ... else</a></span></li><li><span><a href="#The-ternary-operator" data-toc-modified-id="The-ternary-operator-1.5">The ternary operator</a></span></li><li><span><a href="#switch-statement" data-toc-modified-id="switch-statement-1.6"><code>switch</code> statement</a></span></li></ul></li><li><span><a href="#Logical-operators" data-toc-modified-id="Logical-operators-2">Logical operators</a></span></li><li><span><a href="#Loops" data-toc-modified-id="Loops-3">Loops</a></span><ul class="toc-item"><li><span><a href="#while-loop" data-toc-modified-id="while-loop-3.1"><code>while</code> loop</a></span></li><li><span><a href="#do...while-loop" data-toc-modified-id="do...while-loop-3.2"><code>do</code>...<code>while</code> loop</a></span></li><li><span><a href="#for-loop" data-toc-modified-id="for-loop-3.3"><code>for</code> loop</a></span><ul class="toc-item"><li><span><a href="#Historical-for-loop" data-toc-modified-id="Historical-for-loop-3.3.1">Historical <code>for</code> loop</a></span></li><li><span><a href="#New-for-loop" data-toc-modified-id="New-for-loop-3.3.2">New <code>for</code> loop</a></span></li></ul></li><li><span><a href="#continue,-break-and-infinite-loop" data-toc-modified-id="continue,-break-and-infinite-loop-3.4">continue, break and infinite loop</a></span></li><li><span><a href="#So-which-loop-should-I-use?" data-toc-modified-id="So-which-loop-should-I-use?-3.5">So which loop should I use?</a></span></li></ul></li></ul></div>
%% Cell type:markdown id: tags:
## Conditions
%% Cell type:markdown id: tags:
### `if` condition followed by a single statement
%% Cell type:markdown id: tags:
In C++, a condition is the `if` command followed by a condition in parenthesis `()`. The single instruction (that ends at the next `;`) _or_ the block (in braces `{}`) that follows is then executed only if the condition is true.
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int a = 2;
if (a > 0)
std::cout << a << " is greater than 0" << std::endl;
if (a < 0)
std::cout << "This line won't be executed so nothing will be printed." << std::endl;
std::cout << "But this line will be printed: without braces `{}` only the first instruction depends "
"on the condition!" << std::endl;
}
```
%% Cell type:markdown id: tags:
Of course, the precedent code is embarrassing but is due to the poor indenting used: you have to remember that in C++ indenting is just for the programmer: it is much easier to read if a program is properly indented, but it doesn't matter from the compiler standpoint!
Our case above would be much clearer however with a more logical indenting and spacing:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int a = 2;
if (a < 0)
std::cout << "This line won't be executed so nothing will be printed." << std::endl;
std::cout << "It's much clearer now that this line is not encompassed by the condition!" << std::endl;
}
```
%% Cell type:markdown id: tags:
### `if` condition followed by a block
And if you need several lines to be encompassed by the condition, just use braces!
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int a = 2;
if (a < 0)
{
std::cout << "Should not be printed: the condition is false!" << std::endl;
++a; // won't be executed: the condition is false
}
std::cout << "a was not modified and is still 2: " << a << std::endl;
}
```
%% Cell type:markdown id: tags:
### No semicolon at the end of the `if` condition
__BEWARE__: do not put a `;` at the end of an `if` statement! If you do so, the statement executed if the condition is true is the empty statement `;` which does nothing... The risk is rather mitigated: any compiler worth its salt will warn you if you do this mistake.
%% Cell type:code id: tags:
``` C++17
{
int a = 2;
if (a == 0);
std::cout << "Will be printed: the statement after the condition is ';', which does nothing..." << std::endl;
}
```
%% Cell type:code id: tags:
``` C++17
{
int a = 2;
if (a == 2)
; // putting the semicolon in a different line silences the warning; there are legitimate cases in
// which it's useful to do so and the risk is
// slim that this was written by mistake.
}
```
%% Cell type:markdown id: tags:
### if ... else if ... else
It is rather usual to foreseen two (or more...) courses of action depending on the results of one or several conditions. The syntax in this case is the following:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int a = 2;
if (a < 0)
std::cout << a << " is negative." << std::endl;
else if (a == 0)
{
std::cout << a << " is zero." << std::endl;
}
else if (a < 10)
std::cout << a << " is positive but lower than 10." << std::endl;
else if (a < 20)
std::cout << a << " is in the interval [10, 20[." << std::endl;
else
{
std::cout << a << "is greater than 20." << std::endl;
}
}
```
%% Cell type:markdown id: tags:
Please notice that:
* `else if` and `else` syntax is the same as the one for `if` and you may choose a single statement or a block in each branch of the condition.
* As soon as one condition is fulfilled, the execution of the condition blocks ends. I therefore didn't have to spell out in the (`a < 20`) case that `a` had to be greater than 10.
%% Cell type:markdown id: tags:
### The ternary operator
%% Cell type:markdown id: tags:
C++ has inherited from C the so-called ternary operator, which is basically an if/else case packed in one line:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int i = 5;
std::cout << "Is " << i << " even? -> " << (i % 2 == 0 ? "true" : "false") << std::endl;
}
```
%% Cell type:markdown id: tags:
It is really the same as:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int i = 5;
std::cout << "Is " << i << " even? -> ";
if (i % 2 == 0)
std::cout << "true" << std::endl;
else
std::cout << "false" << std::endl;
}
```
%% Cell type:markdown id: tags:
There are nonetheless some cases in which a ternary operator performs a task that is not otherwise reachable by an if/else statement; please consider for instance the initialisation of a `const` variable:
%% Cell type:code id: tags:
``` C++17
{
int i = 5;
const int is_strictly_positive = (i > 0 ? 1 : 0);
std::cout << is_strictly_positive << std::endl;
}
```
%% Cell type:markdown id: tags:
With if/else this doesn't work: both ways to do so that might step in mind are flawed:
%% Cell type:code id: tags:
``` C++17
{
int i = 5;
const int is_strictly_positive;
if (i > 0)
is_strictly_positive = 1; // COMPILATION ERROR: can't assign to const value!
// ...
}
```
%% Cell type:code id: tags:
``` C++17
{
int i = 5;
if (i > 0)
const int is_strictly_positive = 1;
else
const int is_strictly_positive = 0;
std::cout << is_strictly_positive << std::endl; // COMPILATION ERROR: is_strictly_positive not
// in the current scope.
}
```
%% Cell type:markdown id: tags:
### `switch` statement
Very briefly: there is also a `switch` statement that can be used when:
* The variable is an integer, an enum (see [next notebook](./3-Types.ipynb#Enumerations)) or might be convertible into one of those.
* The relationship considered is an equality.
I present it quickly in [appendix](../7-Appendix/Switch.ipynb) but we do not have yet seen all the elements needed to explain its interest (which remains fairly limited compared to the powerful `switch` in other languages...)
%% Cell type:markdown id: tags:
## Logical operators
A condition might be an amalgation of several conditions. The way to glue it is to use logical operators.
The operators are:
* `&&` for the **and** operator.
* `||` for the **or** operator.
* `!` for the **not** operator.
Actually there are so-called __alternative representations__ using the english name directly for each of them but their usage is not widely spread and not advised from a stylistic standpoint (many StackOverflow threads debate on this topic...).
%% Cell type:code id: tags:
``` C++17
#include <iostream>
int a = 2;
int b = 3;
if (a == 2 || b == 5)
std::cout << "Ok: first condition is true." << std::endl;
```
%% Cell type:code id: tags:
``` C++17
if (a == 2 or b == 5)
std::cout << "Same as above illustrating the alternative representation." << std::endl;
```
%% Cell type:code id: tags:
``` C++17
if (a == 2 && b == 5)
std::cout << "Not printed: one condition is false." << std::endl;
```
%% Cell type:code id: tags:
``` C++17
if (!(a < 0))
std::cout << "Ok: a < 0 is false so the `not` operator returns true." << std::endl;
```
%% Cell type:markdown id: tags:
You may combine several of them in a more complex condition. `and` takes precedence over `or`, but anyway it's usually better to disambiguate using parenthesis:
%% Cell type:code id: tags:
``` C++17
if (a == 5 || a == 2 && b == 3)
std::cout << "(a == 5) and (a == 2 && b == 3) are evaluated separately and the latter is true so this text is printed" << std::endl;
```
%% Cell type:code id: tags:
``` C++17
if ((a == 5) || (a == 2 && b == 3))
std::cout << "Same but easier to grasp." << std::endl;
```
%% Cell type:markdown id: tags:
The evaluation of the logical operators is performed from the left to the right.
Builtin operators `&&` and `||` perform short-circuit evaluation: they do not evaluate the second operand if the result is known after evaluating the first. Please notice this means these operators aren't commutative!
%% Cell type:code id: tags:
``` C++17
#include <iostream>
int a = 2;
int b = 3;
if (a < 0 && b++ == 3)
;
std::cout << "b was not incremented!: " << b << std::endl;
```
%% Cell type:code id: tags:
``` C++17
if (b++ == 3 && a < 0)
;
std::cout << "b was incremented: " << b <<
" (as first operation is true second one is evaluated)." << std::endl;
```
%% Cell type:markdown id: tags:
Please notice it's true only for built-in operators: later in this tutorial we will deal with operators overloading, and such operators always evaluate both operands.
%% Cell type:markdown id: tags:
## Loops
%% Cell type:markdown id: tags:
### `while` loop
%% Cell type:markdown id: tags:
The `while` instruction allows you to execute a block of instructions in a loop
as long as a condition is true. The condition is checked **before** each
iteration.
The rules about the instructions belonging to the loop are exactly the same as the ones described for `if` statements.
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int a = 0;
while (a++ < 5)
std::cout << a << std::endl;
}
```
%% Cell type:markdown id: tags:
If the condition is never true in the first place, we never go inside the loop:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int a = 0;
while (a-- < 0)
std::cout << a << std::endl;
}
```
%% Cell type:markdown id: tags:
### `do`...`while` loop
%% Cell type:markdown id: tags:
The `do` instruction allows you to execute a block of instructions in a loop as long as
that a condition is true. The condition is verified **after** each
iteration.
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int a = 0;
do
{
std::cout << a << std::endl;
} while (a++ < 0); // there is a semicolon here.
}
```
%% Cell type:markdown id: tags:
### `for` loop
%% Cell type:markdown id: tags:
#### Historical `for` loop
%% Cell type:markdown id: tags:
The historical `for` instruction allows you to execute a block of instructions as long as a
condition is true; the difference with the `while` loop is that there are fields explicitly detailing:
* The initial situation.
* The condition to check at the end of each loop.
* What should be changed in next loop if one is called for.
Syntax is:
for (_initial situation_ ; _end loop condition_ ; *evolution for next loop*)
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
for (int i = 0; i < 5; ++i)
std::cout << i << std::endl;
}
```
%% Cell type:markdown id: tags:
Any of these fields might be left empty:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int i = 0; // i might also be declared and initialized outside the block
for ( ; i < 5; ) // completely equivalent to `while(i < 5)`
{
++i; // i might also be modified within the block
std::cout << i << std::endl;
}
std::cout << "`for` loop stopped for i = " << i << std::endl;
}
```
%% Cell type:markdown id: tags:
#### New `for` loop
%% Cell type:markdown id: tags:
The `for`syntax we just saw is still very useful, but C++ 11 introduced a new syntax when for instance you want to iterate through all the items in a container, clearly taking inspiration from syntax present in languages such as Python:
for (_type_ _element_ : *container*)
%% Cell type:code id: tags:
``` C++17
#include <iostream>
#include <vector> // we'll present this one more in detail later
{
std::vector<int> v { 2, 3, 5, 7 };
for (int item : v)
std::cout << item << std::endl;
}
```
%% Cell type:markdown id: tags:
It might not seem much, but just for the record doing the same before C++ 11 was not for the faint of heart...:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
#include <vector> // we'll present this one more in detail later
{
std::vector<int> v { 2, 3, 5, 7 }; // and we're cheating here: C++ 03 syntax was here much worse as well...
for (std::vector<int>::const_iterator it = v.cbegin(),
end = v.cend();
it != end;
++it)
{
std::cout << *it << std::endl;
}
}
```
%% Cell type:markdown id: tags:
### continue, break and infinite loop
%% Cell type:markdown id: tags:
A danger with a loop is to make it infinite: you have to make sure an exit way is foreseen:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
// WARNING: if you run this you will have to restart your kernel! (in Kernel menu)
{
int i = 2;
while (i > 0) // condition that is true for quite a long time...
std::cout << ++i << " ";
}
```
%% Cell type:markdown id: tags:
The best is to write a palatable condition to end it, but when some loops become increasingly complex you may have to resort to `break`. Be aware it is slightly frown upon by some programmers, for the very same reasons [goto](https://en.wikipedia.org/wiki/Goto) instructions are avoided.
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int i = 2;
while (i > 0)
{
std::cout << i++ << " ";
if (i > 20)
break;
}
}
```
%% Cell type:markdown id: tags:
In this trivial case writing the condition more properly would be of course much better:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int i = 2;
while (i > 0 && i <= 20)
std::cout << i++ << " ";
}
```
%% Cell type:markdown id: tags:
but honestly in more complex cases `break` can help keep the code more readable.
`continue` is related: it is useful when in some conditions you want to skip the remainder of the current loop iteration to go directly to the next:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
for (int i = 2; i < 20; ++i)
{
if (i == 2)
{
std::cout << i << " is even and prime (hello 2!)." << std::endl;
continue;
}
if (i % 2 == 0)
{
std::cout << i << " is even." << std::endl;
continue; // goes directly at the condition checking step in the loop,
// skipping the remaining code below.
}
std::cout << i << " is odd";
bool is_prime = true;
for (int j = 2; j < i / 2; ++j)
{
if (i % j == 0)
{
is_prime = false;
break; // this break cuts the inner loop 'for (int j = 1; j < i / 2; ++j)'
}
}
std::cout << (is_prime ? " and prime." : ".") << std::endl;
}
}
```
%% Cell type:markdown id: tags:
Of course, in a trivial example like this one we could have written it much more cleanly without any `continue`, but in more complex cases it is really handful to use it: not using it could lead to code much more complicated to understand, and you really should always strive for code that is the most expressive for a reader.
%% Cell type:markdown id: tags:
### So which loop should I use?
%% Cell type:markdown id: tags:
Whichever you want in fact!
They are mostly interchangeable:
* `while` and (historical) `for` are completely interchangeable, as:
- A `while` loop is exactly like a `for ` loop with only the middle term.
- You can transform a `for` loop into a `while` one: putting the first term before the loop and the third one inside the loop to do so.
* `do..while` behaves slightly differently, but you can always mimic the behaviour with another type of loop.
Lots of programming language define these guys (at least `for` and `while`) so it's useful to know about them, but you can choose one and stick with it as well.
%% Cell type:markdown id: tags:
© _CNRS 2016_ - _Inria 2018-2021_
_This notebook is an adaptation of a lecture prepared by David Chamont (CNRS) under the terms of the licence [Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)](http://creativecommons.org/licenses/by-nc-sa/4.0/)_
_The present version has been written by Sébastien Gilles and Vincent Rouvreau (Inria)_
Loading