Mentions légales du service

Skip to content
Snippets Groups Projects
Commit d2b590e5 authored by GILLES Sebastien's avatar GILLES Sebastien
Browse files

Minor changes in the notebooks in procedural programming part, including...

Minor changes in the notebooks in procedural programming part, including renaming of functions to match the same convention (#32).
parent 8f9a83ae
Branches
Tags
1 merge request!43Modifications done while reading again the formation
%% Cell type:markdown id: tags:
# [Getting started in C++](./) - [Procedural programming](./0-main.ipynb) - [Predefined types](./1-Variables.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="#Ordinary-variables" data-toc-modified-id="Ordinary-variables-1">Ordinary variables</a></span><ul class="toc-item"><li><span><a href="#Declaration" data-toc-modified-id="Declaration-1.1">Declaration</a></span></li><li><span><a href="#Initialisation" data-toc-modified-id="Initialisation-1.2">Initialisation</a></span></li><li><span><a href="#Affectation" data-toc-modified-id="Affectation-1.3">Affectation</a></span></li><li><span><a href="#Scope-and-blocks" data-toc-modified-id="Scope-and-blocks-1.4">Scope and blocks</a></span></li><li><span><a href="#Increment-and-decrement-operators" data-toc-modified-id="Increment-and-decrement-operators-1.5">Increment and decrement operators</a></span></li><li><span><a href="#Comparing-values" data-toc-modified-id="Comparing-values-1.6">Comparing values</a></span></li></ul></li><li><span><a href="#References" data-toc-modified-id="References-2">References</a></span></li><li><span><a href="#Pointers" data-toc-modified-id="Pointers-3">Pointers</a></span><ul class="toc-item"><li><span><a href="#Cheatsheet:-pointers-and-reference-syntax" data-toc-modified-id="Cheatsheet:-pointers-and-reference-syntax-3.1">Cheatsheet: pointers and reference syntax</a></span></li><li><span><a href="#Chaining-pointers" data-toc-modified-id="Chaining-pointers-3.2">Chaining pointers</a></span></li><li><span><a href="#nullptr" data-toc-modified-id="nullptr-3.3"><code>nullptr</code></a></span></li></ul></li><li><span><a href="#Constant-variables-and-pointers" data-toc-modified-id="Constant-variables-and-pointers-4">Constant variables and pointers</a></span></li><li><span><a href="#Arrays" data-toc-modified-id="Arrays-5">Arrays</a></span><ul class="toc-item"><li><span><a href="#Arrays-and-pointers" data-toc-modified-id="Arrays-and-pointers-5.1">Arrays and pointers</a></span></li></ul></li></ul></div>
%% Cell type:markdown id: tags:
## Ordinary variables
%% Cell type:markdown id: tags:
### Declaration
%% Cell type:markdown id: tags:
To be usable in a C++ program, a variable must be declared. This declaration shall include at least the type of
the variable, followed by its name and a semicolon.
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int number; // integer variable
double real; // floating-point variable
std::cout << number << std::endl;
std::cout << real << std::endl;
}
```
%% Cell type:markdown id: tags:
### Initialisation
%% Cell type:markdown id: tags:
Although not mandatory, it is **strongly** recommended to give a
Although not mandatory, it is **strongly** recommended to give an
initial value to your variables, as an expression between brackets.
Not providing an initial value may lead to unexpected behaviour. For instance you can't make hypothesis upon the values of `number` and `real` in the cell above: you might end-up with any value... and someone else might get other values on his computer!
Not providing an initial value may lead to unexpected behaviour. For instance you can't make hypothesis upon the values of `number` and `real` in the cell above: you might end-up with any value... and someone else might get other values on his/her computer!
If you give braces without values, a predefined and associated value
to the type is used (usually a form of 0).
%% Cell type:code id: tags:
``` C++17
{
int nb1 { 1 }; // integer variable set with the value 1
int nb2 {}; // same as int nb2{0};
double pi { 3.14 }; // real variable
}
```
%% Cell type:markdown id: tags:
C++ actually supports many other historical forms
of initialization, which you will encounter everywhere, including in this tutorial,
with brackets and/or an equal sign. There are some subtle differences
between each other... that you can ignore most of the time.
between each other... that you can ignore most of the time (still, if you're beginning our advice is to take the habit to use braces which are both less ambiguous and a bit more secure than the others syntaxes).
%% Cell type:code id: tags:
``` C++17
{
int a = 5;
int b(5);
int c { 5 }; // From C++ 11 onward
}
```
%% Cell type:markdown id: tags:
In all cases, even if there is an equal sign, it is important to remember
that it is an initialization, not an assignment (this will be
important when we will define our own types).
%% Cell type:markdown id: tags:
### Affectation
A new value is stored in an existing variable using the operator
of assignment `=`. The name of the variable is on the left; the expression
on the right of the `=` sign is evaluated, and its result is assigned to the variable.
%% Cell type:code id: tags:
``` C++17
#include <iostream> // for std::cout and std::endl
{
int a {}, b {}, c {} ; // default initialization; set the values to 0
std::cout << "Default initialization: a = " << a << ", b = " << b << " and c = " << c << std::endl;
a = 4;
b = 7;
c = a + b;
std::cout << "After affectations: a = " << a << ", b = " << b << " and c = " << c << std::endl;
}
```
%% Cell type:markdown id: tags:
Affectations may be chained:
%% Cell type:code id: tags:
``` C++17
{
int a {}, b {}, c {};
a = b = c = 5;
std::cout << "a = " << a << ", b = " << b << " and c = " << c << std::endl;
}
```
%% Cell type:markdown id: tags:
It is also possible to define (slightly) more advanced operators of assignments that modify the value currently stored by a simple operation:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int a {}, b {}, c {} ; // default initialization; set the values to 0
a += 4; // add 4 to the current value of a
std::cout << "a = " << a << std::endl;
a *= 7; // multiply current value of a by 7
std::cout << "a = " << a << std::endl;
a /= 5; // divide a by 5 and assign the quotient to a
std::cout << "a = " << a << std::endl;
}
```
%% Cell type:markdown id: tags:
### Scope and blocks
A variable is destroyed when it becomes **out of scope**.
The **scope** of a variable is its lifetime: it begins when the variable is declared and ends when it is destroyed, usually when we reach the end of the block where it is defined.
A **block** is essentially what is between braces `{}`.
%% Cell type:code id: tags:
``` C++17
#include <iostream>
int a = 5;
{
std::cout << "a is available at this level: " << a << std::endl;
{
int b = 10;
std::cout << "b is available at this level: " << b << std::endl;
std::cout << "and a is also still available: " << a << std::endl;
} // b becomes out of scope
std::cout << "a is available at this level: " << a << "... but b is not!" << std::endl;
}
```
%% Cell type:markdown id: tags:
Within a same block, a variable name may be only used once:
%% Cell type:code id: tags:
``` C++17
{
int a = 5;
std::cout << "Value = " << a << std::endl;
int a = 1; // COMPILATION ERROR
}
```
%% Cell type:markdown id: tags:
**Beware:** it is however entirely possible to reuse in an inner block a variable name... but it is to be avoided because it really clutters the understanding of the code for a reader! (and compilers usually warn against this, even if here Xeus-cling does not):
%% Cell type:code id: tags:
``` C++17
#include <iostream>
int a = 5;
{
int a = 10;
std::cout << "a is available at this level; it is the most inner scope one: " << a << std::endl;
{
std::cout << "Of course same here: " << a << std::endl;
}
a = a + 5; // this is the inner most 'a' that is modified
} // inner most a becomes out if scope
std::cout << "Here the innermost 'a' got out of scope and we therefore the best choice for 'a'"
"is the initial one: " << a << std::endl;
```
%% Cell type:markdown id: tags:
As seen above, you can declare a block very easily by firing up opening and closing braces. This is an incredibly useful feature: you may thus fine tune the lifetime of your variable to ensure a variable is not used past its prime.
%% Cell type:markdown id: tags:
### Increment and decrement operators
Finally, C++ also provides a shortcut when value is either incremented or decremented by adding `++` or `--` before or after the name of the variable.
* If the sign is placed before the variable, it is a **pre-increment**.
* If the sign is placed after the variable, it is a **post-increment**.
An example is the better way to explain the difference between both:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
int a = 5;
int b = 3;
a++; // increment a by 1.
++a; // same, both are actually equivalents here.
int c = a + b;
std::cout << "a = " << a << ", b = " << b << " and c = " << c << std::endl;
```
%% Cell type:code id: tags:
``` C++17
c = a-- + b; // first a + b is evaluated, and only then a is decremented.
std::cout << "a = " << a << ", b = " << b << " and c = " << c << std::endl;
```
%% Cell type:code id: tags:
``` C++17
c = a + ++b; // first b is incremented, and only then a + b is evaluated.
std::cout << "a = " << a << ", b = " << b << " and c = " << c << std::endl;
```
%% Cell type:markdown id: tags:
Honestly it's usually better to remove any ambiguity by separating explicitly both operations:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int a = 7;
int b = 3;
int c = a + b;
std::cout << "a = " << a << ", b = " << b << " and c = " << c << std::endl;
c = a + b;
--a; // equivalent to a-- but for reasons related to the standard library I advise you
// to rather use the pre-increment form when both are equivalent.
std::cout << "a = " << a << ", b = " << b << " and c = " << c << std::endl;
++b; // same: equivalent to b++;
c = a + b;
std::cout << "a = " << a << ", b = " << b << " and c = " << c << std::endl;
}
```
%% Cell type:markdown id: tags:
### Comparing values
As shown above, `=` is the affectation operator. To compare two values, the symbol to use is `==`.
Other arithmetic operators are:
| Operator | Effect |
|:------------- |:-------------------------------------------:|
| `a == b` | `true` if a and b are equals |
| `a != b` | `true` if a and b are different |
| `a < b` | `true` if a is less than b |
| `a > b` | `true` if a is greater than b |
| `a >= b` | `true` if a is greater than or equal to b |
| `a <= b` | `true` if a is less than or equal to b |
These operators are defined for most ordinary types and may be defined for your own types (we'll see that [later](../3-Operators/2-Comparison.ipynb)).
%% Cell type:markdown id: tags:
## References
A reference is a variable that acts as a kind of alias, and provides another name for the same variable.
When defining a reference, it must be **immediately** initialized by
indicating to which variable it should point; it cannot be changed after that.
The syntax is to add a `&` character just after the type:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
int a { 2 };
int b { a };
int& c { a }; // c is a reference to a
std::cout << "Initial values : a = " << a << ", b = " << b << " and c = " << c << std::endl;
```
%% Cell type:code id: tags:
``` C++17
a = -7;
std::cout << "Modify a : a = " << a << ", b = " << b << " and c = " << c << std::endl;
```
%% Cell type:code id: tags:
``` C++17
b = 42;
std::cout << "Modify b : a = " << a << ", b = " << b << " and c = " << c << std::endl;
```
%% Cell type:code id: tags:
``` C++17
c = 0;
std::cout << "Modify c : a = " << a << ", b = " << b << " and c = " << c << std::endl;
```
%% Cell type:markdown id: tags:
Reference is a purely C++ concept that doesn't exist in C.
%% Cell type:markdown id: tags:
## Pointers
A pointer contains the address of another variable. It declares itself by slipping
A pointer contains the address in memory of another variable. It declares itself by slipping
a `*` character before the name. It can be initialized or not with the address
of another variable. To explicitly extract the address of this other variable,
we use the symbol `&`.
%% Cell type:code id: tags:
``` C++17
#include <iostream>
int a { 2 };
int* p {&a}; // define a pointer p which is initialized with the address of a
std::cout << "a = " << a << std::endl;
std::cout << "p = " << p << std::endl;
```
%% Cell type:markdown id: tags:
You may ask the underlying value at a given address with `*` (I reckon: this syntax may be _very_ confusing at first...)
%% Cell type:code id: tags:
``` C++17
std::cout << "Value stored at p = " << *p << std::endl;
```
%% Cell type:markdown id: tags:
The `*` syntax may be used to modify the underlying value:
%% Cell type:code id: tags:
``` C++17
*p = *p + 5;
std::cout << "After the operation: pointer " << p << " stores the value " << *p << std::endl;
```
%% Cell type:markdown id: tags:
Pointers may be used as variables by themselves and see their actual content changed during execution of a program:
%% Cell type:code id: tags:
``` C++17
int b { 3 };
p = &b;
std::cout << "After the pointer assignation: pointer " << p << " stores the value " << *p << std::endl;
std::cout << "(and value of a remains unchanged: " << a << ')' << std::endl;
```
%% Cell type:markdown id: tags:
We can therefore see the pointer as a kind of redefinable reference (pointers are a feature from C language whereas references are a C++ specific feature).
%% Cell type:markdown id: tags:
### Cheatsheet: pointers and reference syntax
%% Cell type:markdown id: tags:
| Applied to: | A type `T` | A variable `x`
|:------------- |:-------------------------------------------:|:-------------------------------------------:|
| * | Pointer to an object of type `T` | Variable under `x` if `x` is a pointer, invalid code otherwise |
| & | Reference to an object of type `T` | Address of the variable (i.e. a pointer) |
%% Cell type:markdown id: tags:
### Chaining pointers
It is possible to chain pointers:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int n = 5;
int* p = &n;
int** q = &p;
std::cout << "*q is a pointer to an int (i.e. an address): " << *q << " - it is the same as what is stored in p: " << p << std::endl;
std::cout << "**q gives the original `n` value: " << **q << std::endl;
}
```
%% Cell type:markdown id: tags:
This was something that was very common in C, but that I do not recommend in C++ as there are much better ways to achieve the same goals (that we do not have seen yet, so don't panic if you do not understand why we would need this).
%% Cell type:markdown id: tags:
### `nullptr`
A pointer can also designate no variables if initialized with the special value
`nullptr`. It is then a mistake to try to access its pointed value (as we shall see later, an [`assert`](../5-UsefulConceptsAndSTL/1-ErrorHandling.ipynb#Assert) is a good idea to ensure we do not try to dereference a `nullptr` value).
It is strongly recommended to initialize a pointer when creating it: if you define an uninitialized pointer, it points to an arbitrary area of memory, which can create undefined behaviors that are not necessarily reproducible.
If the pointed area is known at initialization and never changes throughout the program, you should consider a reference rather than a pointer.
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int* p = nullptr; // define a null pointer p
std::cout << "\t p: address = " << p << ", value = " << *p << std::endl; // Dereferencing p is misguided!
}
```
%% Cell type:markdown id: tags:
`nullptr` was introduced in C++ 11; if you're working with a legacy codebase that doesn't use this standard use `NULL` (but stick to `nullptr` for modern code!)
%% Cell type:markdown id: tags:
## Constant variables and pointers
When declaring a variable of a predefined type, it is possible to specify its value can't be changed afterward by using the word `const` which may be placed before or after the type:
%% Cell type:code id: tags:
``` C++17
const double pi = 3.1415927;
double const pi_2 = 3.1415927; // equally valid; it is just a matter of taste. Mine is to put it before,
// so that is what you will see in the remaining of the lecture.
// There are however very persuasive arguments for the other convention:
// see http://slashslash.info/eastconst/
```
%% Cell type:code id: tags:
``` C++17
pi = 5.; // COMPILATION ERROR!
```
%% Cell type:code id: tags:
``` C++17
pi_2 = 7.; // COMPILATION ERROR!
```
%% Cell type:markdown id: tags:
In the case of a pointer, we can declare the pointer itself constant, and/or the value pointed to depending on whether we place the keyword `const` before or after the type name (in this case it applies to the pointed value) or after the character `*` (in this case it applies to the pointer itself):
%% Cell type:code id: tags:
``` C++17
{
int a { 2 }, b { 3 };
int* p { &a }; // Both pointer and pointed values are modifiable.
p = &b; // OK
*p = 5; // OK
}
```
%% Cell type:code id: tags:
``` C++17
int a { 2 }, b { 3 };
int* const p { &a }; // Value is modifiable, but not the address pointed to.
*p = 5; // OK
```
%% Cell type:code id: tags:
``` C++17
p = &b; // COMPILATION ERROR
```
%% Cell type:code id: tags:
``` C++17
const int* p { &a }; // Address pointed to is modifiable, but not the underlying value.
p = &b; // OK
```
%% Cell type:code id: tags:
``` C++17
*p = 5; // COMPILATION ERROR
```
%% Cell type:code id: tags:
``` C++17
int a { 2 }, b { 3 };
const int* const p { &a }; // Nothing is modifiable
p = &b; // COMPILATION ERROR
```
%% Cell type:code id: tags:
``` C++17
*p = 5; // COMPILATION ERROR
```
%% Cell type:markdown id: tags:
**IMPORTANT**: Even if declared `const`, the pointed value is
not intrinsically constant. It just can't be
modified through this precise pointer.
If other variables reference the same memory area and
are not constant, they are able to modify the value:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int a { 2 }, b { 3 };
const int* p { &a }; // Address pointed to is modifiable, but not the underlying value.
std::cout << "Value pointed by p (which doesn't allow value modification) is: " << *p << std::endl;
std::cout << "Value pointed by pointer p (which doesn't allow value modification) is: " << *p << std::endl;
int* p2 {&a}; // Pointer to the same memory area, but no constness here.
*p2 = 10;
std::cout << "Value pointed by p (and modified through p2) is: " << *p << std::endl;
std::cout << "Value pointed by pointer p (and modified through p2) is: " << *p << std::endl;
a = -3;
std::cout << "Value pointed by pointer p (and modified through variable directly) is: " << *p << std::endl;
}
```
%% Cell type:markdown id: tags:
On the other hand, pointers can't be used as a work-around to modify a constant value:
%% Cell type:code id: tags:
``` C++17
const int n { 3 };
```
%% Cell type:code id: tags:
``` C++17
int* p { &n }; // COMPILATION ERROR
```
%% Cell type:code id: tags:
``` C++17
const int* p_n2 { &n }; // OK
```
%% Cell type:markdown id: tags:
## Arrays
The operator `[]` enables the creation of an array.
**Beware:** In C++, the indexes of an array start at 0.
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int i[10] ; // Array of 10 integers - not initialised properly!
double x[3] = { 1., 2., 3. } ; // Array of 3 reals, C++ 11 syntax
std::cout << "i[2] = " << i[2] << " (may be gibberish: undefined behaviour due to lack of initialization!)" << std::endl;
std::cout << "i[10] = " << i[10] << " (undefined behaviour: out of range. Warning identifies the issue)" << std::endl ;
std::cout << "x[1] = " << x[1] << " (expected: 2.)" << std::endl ;
}
```
%% Cell type:markdown id: tags:
Multi-dimensional arrays are also possible:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int k[2][3] { { 5, 7, 0 }, { 3, 8, 9 }};
int k[2][3] { { 5, 7, 0 }, { 3, 8, 9 } };
std::cout << "k[0][0] = " << k[0][0] << " (expected: 5)" << std::endl;
std::cout << "k[1][2] = " << k[1][2] << " (expected: 9)" << std::endl;
int l[2][3] {}; // default initialization of all elements of the array.
std::cout << "l[0][0] = " << l[0][0] << " (expected: 0)" << std::endl;
std::cout << "l[1][2] = " << l[1][2] << " (expected: 0)" << std::endl;
}
```
%% Cell type:markdown id: tags:
**IMPORTANT**: so far we have considered only the case of _static_ arrays, for which the size is already known at compilation. We will deal with dynamic ones [later in this tutorial](./5-DynamicAllocation.ipynb#Arrays-on-heap) (and also with the standard libraries [alternatives](../5-UsefulConceptsAndSTL/3-Containers.ipynb) such as `std::vector` or `std::array` which are actually much more compelling).
%% Cell type:markdown id: tags:
### Arrays and pointers
The variable designating an array is similar to a constant pointer pointing to the beginning of the array. Increasing this pointer is like moving around in the array.
%% Cell type:code id: tags:
``` C++17
#include <iostream>
int i[2] = { 10, 20 } ;
```
%% Cell type:code id: tags:
``` C++17
std::cout << *i << " (expected: 10)" << std::endl;
```
%% Cell type:code id: tags:
``` C++17
std::cout << *i + 1 << " (expected: 11)" << std::endl;
```
%% Cell type:code id: tags:
``` C++17
std::cout << *(i + 1) << " (expected: 20)" << std::endl;
```
%% Cell type:code id: tags:
``` C++17
int* j = i; // OK!
++j; // OK!
std::cout << "Value pointed by j is " << *j << " (expected: 20)" << std::endl;
```
%% Cell type:markdown id: tags:
© _CNRS 2016_ - _Inria 2018-2022_
_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)_
......
%% 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...)
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 vastly more 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.
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!
Please notice this means these operators are **not 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
The story is different if the ordering of the conditions to test is inverted:
```
%% 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.
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:
`continue` is related: it is useful when in some conditions you want to skip the rest 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)
if (i % j == 0) // % returns the remainder of the division
{
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;
std::cout << (is_prime ? " and prime." : ".") << std::endl; // the ternary operator
}
}
```
%% 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-2022_
_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)_
......
%% Cell type:markdown id: tags:
# [Getting started in C++](./) - [Procedural programming](./0-main.ipynb) - [Predefined types](./3-Types.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="#Boolean" data-toc-modified-id="Boolean-1">Boolean</a></span></li><li><span><a href="#Enumerations" data-toc-modified-id="Enumerations-2">Enumerations</a></span><ul class="toc-item"><li><span><a href="#Historical-enumerations" data-toc-modified-id="Historical-enumerations-2.1">Historical enumerations</a></span></li><li><span><a href="#New-enumerations" data-toc-modified-id="New-enumerations-2.2">New enumerations</a></span></li></ul></li><li><span><a href="#Numerical-types" data-toc-modified-id="Numerical-types-3">Numerical types</a></span><ul class="toc-item"><li><ul class="toc-item"><li><span><a href="#List-of-numerical-types" data-toc-modified-id="List-of-numerical-types-3.0.1">List of numerical types</a></span></li><li><span><a href="#Numeric-limits" data-toc-modified-id="Numeric-limits-3.0.2">Numeric limits</a></span></li><li><span><a href="#Conversions-between-digital-types" data-toc-modified-id="Conversions-between-digital-types-3.0.3">Conversions between digital types</a></span></li></ul></li><li><span><a href="#Explicit-conversions-inherited-from-C" data-toc-modified-id="Explicit-conversions-inherited-from-C-3.1">Explicit conversions inherited from C</a></span></li><li><span><a href="#Explicit-conversions-by-static_cast" data-toc-modified-id="Explicit-conversions-by-static_cast-3.2">Explicit conversions by static_cast</a></span></li><li><span><a href="#Other-explicit-conversions" data-toc-modified-id="Other-explicit-conversions-3.3">Other explicit conversions</a></span></li></ul></li><li><span><a href="#Characters-and-strings" data-toc-modified-id="Characters-and-strings-4">Characters and strings</a></span><ul class="toc-item"><li><span><a href="#Historical-strings" data-toc-modified-id="Historical-strings-4.1">Historical strings</a></span></li><li><span><a href="#std::string" data-toc-modified-id="std::string-4.2">std::string</a></span></li></ul></li><li><span><a href="#Renaming-types" data-toc-modified-id="Renaming-types-5">Renaming types</a></span></li><li><span><a href="#decltype-and-auto" data-toc-modified-id="decltype-and-auto-6"><code>decltype</code> and <code>auto</code></a></span></li></ul></div>
%% Cell type:markdown id: tags:
## Boolean
Variables with type `bool` may be set to true or false.
It should be noted that this type did not originally exist, and that C++ instructions with conditions do not necessarily expect boolean values, but rather integers.
There is a form of equivalence between booleans and integers: any null integer is equivalent to `false`, and any other value is equivalent to `true`.
%% Cell type:code id: tags:
``` C++17
#include <iostream>
bool undefined; // UNDEFINED !!
if (undefined)
std::cout << "This text might appear or not - it's truly undefined and may vary from "
"one run/compiler/architecture/etc... to another!" << std::endl;
```
%% Cell type:code id: tags:
``` C++17
bool defined { true };
if (defined)
std::cout << "Defined!" << std::endl;
```
%% Cell type:code id: tags:
``` C++17
int n = -5;
if (n)
std::cout << "Boolean value of " << n << " is true." << std::endl;
```
%% Cell type:code id: tags:
``` C++17
int n = 0;
if (!n) // ! is the not operator: the condition is true if n is false.
std::cout << "Boolean value of " << n << " is false." << std::endl;
```
%% Cell type:markdown id: tags:
## Enumerations
### Historical enumerations
The historical enumerations `enum` of C++ allow to define constants that are treated as integers, and that can be initialized from integers. By default the first value is 0 and the `enum` is incremented for each value, but it is possible to bypass these default values and provide the desired numerical value yourself.
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
enum color { red, green, blue } ;
std::cout << red << " " << green << " " << blue << " (expected: 0, 1, 2)" << std::endl;
enum shape { circle=10, square, triangle=20 };
std::cout << circle << " " << square << " " << triangle << " (expected: 10, 11, 20)"<< std::endl; // 10 11 20
}
```
%% Cell type:markdown id: tags:
These `enum` are placeholders for integers and might be used as such:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
enum color { red, green, blue } ;
int a { 5 };
color c = green;
int b = a + c;
std::cout << "b = " << b << " (expected: 6)" << std::endl;
enum shape { circle=10, square, triangle=20 };
shape s = triangle;
int d = s + c;
std::cout << "d = " << d << " (expected: 21... but we've just added a shape to a color without ado!)" << std::endl;
}
```
%% Cell type:markdown id: tags:
A shortcoming of historical `enum ` is that the same word can't be used in two different `enum`:
A (huge) shortcoming of historical `enum ` is that the same word can't be used in two different `enum`:
%% Cell type:code id: tags:
``` C++17
{
enum is_positive { yes, no };
enum is_colored { yes, no }; // COMPILATION ERROR!
}
```
%% Cell type:markdown id: tags:
### New enumerations
To overcome the two limitations we have just mentioned, C++11 makes it possible to declare new `enum class` enumerations, each constituting a separate type, not implicitly convertible into an integer. This type protects against previous errors at the cost of a little more writing work.
%% Cell type:code id: tags:
``` C++17
enum class is_positive { yes, no };
enum class is_colored { yes, no }; // OK
```
%% Cell type:code id: tags:
``` C++17
yes; // COMPILATION ERROR: `enum class ` value must be prefixed! (see below)
```
%% Cell type:code id: tags:
``` C++17
is_positive p = is_positive::yes; // OK
```
%% Cell type:code id: tags:
``` C++17
int a = is_positive::no; // COMPILATION ERROR: not implicitly convertible into an integer
```
%% Cell type:code id: tags:
``` C++17
is_positive::yes + is_colored::no; // COMPILATION ERROR: addition of two unrelated types
```
%% Cell type:code id: tags:
``` C++17
{
enum class color { red, green, blue } ;
color c = color::green;
bool is_more_than_red = (c > color::red); // Both belong to the same type and therefore might be compared
}
```
%% Cell type:markdown id: tags:
These enum types are really handy to make code more expressive, especially in function calls:
````
f(print::yes, perform_checks::no);
````
is much more expressive (and less error-prone) than:
````
f(true, false);
````
for which you will probably need to go check the prototype to figure out what each argument stands for.
As we shall see [shortly](#Explicit-conversions-by-static_cast), you may perform arithmetic with the underlying integer through _explicit cast_ of the enum into an integer.
%% Cell type:markdown id: tags:
## Numerical types
#### List of numerical types
The FORTRAN correspondences below are given as examples. The
size of the C++ digital types can vary depending on the processor used. The
standard C++ only imposes `short <= int <= long` and `float <= double <= long double`. This makes these predefined types unportable. Like many things
in C, and therefore in C++, performance is given priority over any other consideration.
The default integer and real types, `int` and `double`, are assumed
to match the size of the processor registers and be the fastest (for more details see [the article on cppreference](http://en.cppreference.com/w/cpp/language/types))
| C++ | Fortran | Observations | 0 notation |
|:------------- |:---------:|:-------------------:|:----------:|
| `short` | INTEGER*2 | At least on 16 bits | None |
| `int` | INTEGER*4 | At least on 16 bits | 0 |
| `long` | INTEGER*8 | At least on 32 bits | 0l |
| `long long` | INTEGER*16| At least on 64 bits | 0ll |
| `float` | REAL*4 | - | 0.f |
| `double` | REAL*8 | - | 0. |
| `long double` | REAL*16 | - | 0.l |
All integer types (`short`, `int` and `long`) also have an unsigned variant, for example
`unsigned int`, which only takes positive values.
It should also be noted that the type `char` is the equivalent of one byte,
and depending on the context will be interpreted as a number or as a
character.
If you need an integer type of a defined size, regardless of the type of processor or platform used, you should use those already defined in `<cstdint>` for C++11 (for more details click [here](http://en.cppreference.com/w/cpp/types/integer)).
The _0 notation column_ is the way to notice explicitly the type in an expression; of course any value might be used instead of 0. A `u` might be used to signal the unsigned status for integer types; for instance `3ul` means 3 as an _unsigned long_. `auto` notation below will illustrate a case in which such a notation is useful.
The STL features rather heavily a type named `std::size_t`, which by design is able to store the maximum size of a theoretically possible object of any type (including array). On most (all?) systems `std::size_t` is an alias to an `unsigned long`. More may be found about this type on [CppReference](https://en.cppreference.com/w/cpp/types/size_t).
The STL features rather heavily a type named `std::size_t`, which by design is able to store the maximum size of a theoretically possible object of any type (including array). On most (all?) systems `std::size_t` is an alias to an `unsigned long`. More may be found about this type on [CppReference](https://en.cppreference.com/w/cpp/types/size_t). The equivalent counterpart for signed integers is the [`std::ptrdiff_t`](https://en.cppreference.com/w/cpp/types/ptrdiff_t), which is the signed integer type of the result of subtracting two pointers.
You might also encounter [`std::ptrdiff_t`](https://en.cppreference.com/w/cpp/types/ptrdiff_t), which is akin to `std::size_t` but for *signed* values. It is typically used in the STL to store the result of subtracting two pointers.
#### Numeric limits
Always keep in mind the types of the computer don't match the abstract concept you may use in mathematics... See [link](https://en.cppreference.com/w/cpp/types/numeric_limits). The types stored especially don't go from minus infinity to infinity:
Always keep in mind the types of the computer don't match the abstract concept you may use in mathematics... The types stored especially don't go from minus infinity to infinity:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
#include <limits> // for std::numeric_limits
{
std::cout << "int [min, max] = [" << std::numeric_limits<int>::lowest() << ", "
<< std::numeric_limits<int>::max() << "]" << std::endl;
std::cout << "unsigned int [min, max] = [" << std::numeric_limits<unsigned int>::lowest() << ", "
<< std::numeric_limits<unsigned int>::max() << "]" << std::endl;
std::cout << "short [min, max] = [" << std::numeric_limits<short>::lowest() << ", "
<< std::numeric_limits<short>::max() << "]" << std::endl;
std::cout << "long [min, max] = [" << std::numeric_limits<long>::lowest() << ", "
<< std::numeric_limits<long>::max() << "]" << std::endl;
std::cout << "float [min, max] = [" << std::numeric_limits<float>::lowest() << ", "
<< std::numeric_limits<float>::max() << "]" << std::endl;
std::cout << "double [min, max] = [" << std::numeric_limits<double>::lowest() << ", "
<< std::numeric_limits<double>::max() << "]" << std::endl;
std::cout << "long double [min, max] = [" << std::numeric_limits<long double>::lowest() << ", "
<< std::numeric_limits<long double>::max() << "]" << std::endl;
}
```
%% Cell type:markdown id: tags:
(see the [CppReference dedicated page](https://en.cppreference.com/w/cpp/types/numeric_limits) for more details about `std::numeric_limits`).
If an initial value is not in the range, the compiler will yell:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
short s = -33010; // triggers a warning: outside the range
std::cout << s << std::endl;
}
```
%% Cell type:markdown id: tags:
However, if you go beyond the numeric limit during a computation you're on your own:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
#include <limits> // for std::numeric_limits
{
unsigned int max = std::numeric_limits<unsigned int>::max();
std::cout << "Max = " << max << std::endl;
std::cout << "Max + 1 = " << max + 1 << "!" << std::endl;
std::cout << "Max + 1 = " << max + 1 << " // !" << std::endl;
}
```
%% Cell type:markdown id: tags:
When you reach the end of a type, a modulo is actually applied to make put it back into the range!
Don't worry, for most computations you shouldn't run into this kind of trouble, but if you are dealing with important values it is important to keep in mind this kind of issues.
The most obvious way to avoid this is to choose appropriate types: if your integer might be huge a `long` is more appropriate than an `int`.
Other languages such as Python gets a underlying integer model that is resilient to this kind of issue but there is a cost behind; types such as those used in C++ are tailored to favor optimization on your hardware.
Other languages such as Python gets a underlying integer model that is resilient to this kind of issue but there is a cost behind it; types such as those used in C++ are tailored to favor optimization on your hardware.
%% Cell type:markdown id: tags:
#### Conversions between digital types
[Earlier](/notebooks/1-ProceduralProgramming/1-Variables.ipynb#Initialisation) I indicated there were small differences between the three initialization methods, that could be ignored most of the time.
The difference is related to implicit conversion: both historical initialization methods are ok with implicit conversion __with accuracy loss__:
%% Cell type:code id: tags:
``` C++17
{
float f = 1.12345678901234567890;
double d = 2.12345678901234567890;
float f_d(d);
float f_dd = d;
}
```
%% Cell type:markdown id: tags:
whereas C++ 11 introduced initialization with braces isn't:
%% Cell type:code id: tags:
``` C++17
{
double d = 2.12345678901234567890;
float f_d{d}; // COMPILATION ERROR
}
```
%% Cell type:markdown id: tags:
This is really related to **accuracy loss**: initialization with braces is ok if there are none:
%% Cell type:code id: tags:
``` C++17
{
float f = 1.12345678901234567890;
double d_f { f }; // OK
}
```
%% Cell type:markdown id: tags:
Accuracy losses are detected during conversion:
* from a floating point type (`long double`, `double` and `float`) into an integer type.
* from a `long double` into a `double` or a `float`, unless the source is constant and its value fits into the type of the destination.
* from a `double` into a `float`, unless the source is constant and its value fits in the type of the destination.
* from an integer type to an enumerated or floating point type, unless the source is constant and its value fits into the type of the destination.
* from an integer type to an enumerated type or another integer type, unless the source is constant and its value fits into the type of the destination.
%% Cell type:markdown id: tags:
### Explicit conversions inherited from C
In the case of an explicit conversion, the programmer explicitly says which conversion to use.
C++ inherits the forcing mechanism of the C type:
%% Cell type:code id: tags:
``` C++17
{
unsigned short i = 42000 ;
short j = short(i) ;
unsigned short k = (unsigned short)(j) ;
}
```
%% Cell type:markdown id: tags:
It is **not recommended** to use this type of conversion: even if it is clearly faster to type, it can perform a different C++ conversion under the hood and does not stand out clearly when reading a code; it is preferable to use the other conversion modes mentioned below.
%% Cell type:markdown id: tags:
### Explicit conversions by static_cast
C++ has also redefined a family of type forcing,
more verbose but more precise. The most common type of explicit conversion is the `static_cast`:
%% Cell type:code id: tags:
``` C++17
{
unsigned short i = 42000;
short j = static_cast<short>(i);
unsigned short k = static_cast<unsigned short>(j);
}
```
%% Cell type:markdown id: tags:
Another advantage of this more verbosy syntax is that you may find it more easily in your code with your editor search functionality.
%% Cell type:markdown id: tags:
### Other explicit conversions
There are 3 other types of C++ conversions:
* `const_cast`, to add or remove constness to a reference or a pointer (obviously to be used with great caution!)
* `dynamic_cast`, which will be introduced when we'll deal with polymorphism.
* `reinterpret_cast`, which is a very brutal cast which changes the type into any other type, regardless of the compatibility of the two types considered. It is a dangerous one that should be considered only in very last resort (usually when interacting with a C library).
%% Cell type:markdown id: tags:
## Characters and strings
### Historical strings
In C, a character string is literally an array of `char` variables, the last character of which is by convention the symbol `\0`.
The `strlen` function returns the length of a string, which is the number of characters between the very first character and the first occurrence of `\0`.
The `strcpy` function copies a character string to a new memory location; care must be taken to ensure that the destination is large enough to avoid any undefined behavior.
The `strncpy` function allows you to copy only the first <b>n</b> first characters, where <b>n</b> is the third parameter of the function. Same remark about the need to foresee a large enough destination.
%% Cell type:code id: tags:
``` C++17
#include <iostream>
#include <cstring> // For strlen, strcpy, strncpy
char hello[] = {'h','e','l','l','o', '\0'};
char copy[6] = {}; // = {'\0','\0','\0','\0','\0','\0' };
strcpy(copy, hello);
std::cout << "String '" << copy << "' is " << strlen(copy) << " characters long." << std::endl;
```
%% Cell type:code id: tags:
``` C++17
const char* hi = "hi"; // Not putting the const here triggers a warning.
strncpy(copy, hi, strlen(hi));
copy[strlen(hi)] = '\0'; // Don't forget to terminate the string!
std::cout << "String '" << copy << "' is " << strlen(copy) << " characters long." << std::endl;
```
%% Cell type:markdown id: tags:
There are several other functions related to historical strings; for more information, do not hesitate to consult [this reference page](http://www.cplusplus.com/reference/cstring/).
%% Cell type:markdown id: tags:
### std::string
In modern C++, rather than bothering with character tables
which come from the C language, it's easier to use the type `std::string`, provided
through the standard language library, that provides a much simpler syntax:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
#include <cstring> // For strlen
#include <string> // For std::string
const char* hello_str = "hello";
std::string hello = hello_str;
std::string hi("hi");
std::string copy {};
```
%% Cell type:code id: tags:
``` C++17
copy = hello; // please notice affectation is much more straightforward
std::cout << "String '" << copy << "' is " << copy.length() << " characters long." << std::endl;
```
%% Cell type:code id: tags:
``` C++17
const char* copy_str = copy.data(); // Returns a classic C-string (from C++11 onward)
std::cout << "String '" << copy_str << "' is " << strlen(copy_str) << " characters long." << std::endl;
```
%% Cell type:code id: tags:
``` C++17
const char* old_copy_str = &copy[0]; // Same before C++11...
std::cout << "String '" << old_copy_str << "' is " << strlen(old_copy_str) << " characters long." << std::endl;
```
%% Cell type:code id: tags:
``` C++17
std::string dynamic {"dynamic std::string"};
std::cout << "String '" << dynamic << "' is " << dynamic.length() << " characters long." << std::endl;
```
%% Cell type:code id: tags:
``` C++17
dynamic = "std::string is dynamical and flexible";
std::cout << "String '" << dynamic << "' is " << dynamic.length() << " characters long." << std::endl;
```
%% Cell type:markdown id: tags:
If needed (for instance to interact with a C library) you may access to the underlying table with `c_str()` or `data()` (both are interchangeable):
%% Cell type:code id: tags:
``` C++17
#include <string>
{
std::string cplusplus_string("C++ string!");
const char* c_string = cplusplus_string.c_str();
const char* c_string_2 = cplusplus_string.data();
}
```
%% Cell type:markdown id: tags:
The `const` here is important: you may access the content but should not modify it; this functionality is provided for read-only access.
%% Cell type:markdown id: tags:
FYI, C++17 introduced [std::string_view](https://en.cppreference.com/w/cpp/header/string_view) which is more efficient than `std::string` for some operations (it is presented [in appendix](../7-Appendix/StringView.ipynb) but if it's your first reading it's a bit early to tackle it now).
%% Cell type:markdown id: tags:
## Renaming types
Sometimes it may be handy to rename a type, for instance if you want to be able to change easily throughout the code the numeric precision to use. Historical syntax (up to C++ 11 and still valid) was `typedef`:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
#include <iomanip> // For std::setprecision
{
typedef double real; // notice the ordering: new typename comes after its value
real radius {1.};
real area = 3.1415926535897932385 * radius * radius;
std::cout <<"Area = " << std::setprecision(15) << area << std::endl;
}
```
%% Cell type:markdown id: tags:
In more modern C++ (C++11 and above), another syntax relying on `using` keyword was introduced; it is advised to use it as this syntax is more powerful in some contexts (see later with templates...):
%% Cell type:code id: tags:
``` C++17
#include <iostream>
#include <iomanip> // For std::setprecision
{
using real = float; // notice the ordering: more in line with was we're accustomed to when
// initialising variables.
real radius {1.};
real area = 3.1415926535897932385 * radius * radius;
std::cout <<"Area = " << std::setprecision(15) << area << std::endl;
}
```
%% Cell type:markdown id: tags:
## `decltype` and `auto`
C++ 11 introduced new keywords that are very handy to deal with types:
* `decltype` which is able to determine **at compile time** the underlying type of a variable.
* `auto` which determines automatically **at compile time** the type of an expression.
%% Cell type:code id: tags:
``` C++17
#include <vector>
{
auto i = 5; // i is here an int.
auto j = 5u; // j is an unsigned int
decltype(j) k; // decltype(j) is interpreted by the compiler as an unsigned int.
}
```
%% Cell type:markdown id: tags:
On such trivial examples it might not seem much, but in practice it might prove incredibly useful. Consider for instance the following versions of the code for iterating over a vector with an 'historical' `for` loop (the details don't matter: we'll deal with `std::vector` in a [later notebook](../5-UsefulConceptsAndSTL/3-Containers.ipynb)):
%% Cell type:code id: tags:
``` C++17
#include <vector>
#include <iostream>
{
std::vector<unsigned int> primes { 2, 3, 5 };
// C++ 03 way of iterating over the content of a vector.
for (std::vector<unsigned int>::const_iterator it = primes.cbegin();
it != primes.cend();
++it)
{
std::cout << *it << " is prime." << std::endl;
}
}
```
%% Cell type:markdown id: tags:
It's very verbosy; we could of course use alias:
%% Cell type:code id: tags:
``` C++17
#include <vector>
#include <iostream>
{
std::vector<unsigned int> primes { 2, 3, 5 };
using iterator = std::vector<unsigned int>::const_iterator;
// C++ 03 way of iterating over the content of a vector - with an alias
for (iterator it = primes.cbegin();
it != primes.cend();
++it)
{
std::cout << *it << " is prime." << std::endl;
}
}
```
%% Cell type:markdown id: tags:
But with `decltype` we may write instead:
%% Cell type:code id: tags:
``` C++17
#include <vector>
#include <iostream>
{
std::vector<unsigned int> primes { 2, 3, 5 };
// C++ 11 decltype
for (decltype(primes.cbegin()) it = primes.cbegin();
it != primes.cend();
++it)
{
std::cout << *it << " is prime." << std::endl;
}
}
```
%% Cell type:markdown id: tags:
or even better:
%% Cell type:code id: tags:
``` C++17
#include <vector>
#include <iostream>
{
std::vector<unsigned int> primes { 2, 3, 5 };
// C++ 11 auto
for (auto it = primes.cbegin();
it != primes.cend();
++it)
{
std::cout << *it << " is prime." << std::endl;
}
}
```
%% Cell type:markdown id: tags:
That is not to say `decltype` is always inferior to `auto`: there are some cases in which decltype is invaluable (especially in metaprogramming, but it's mostly out of the scope of this lecture - we'll skim briefly over it in a later [notebook](../4-Templates/4-Metaprogramming.ipynb)).
C++ 14 introduced a new one (poorly) called `decltype(auto)` which usefulness will be explained below:
%% Cell type:code id: tags:
``` C++17
#include <algorithm>
#include <iostream>
int i = 5;
int& j = i;
auto k = j;
if (std::is_same<decltype(j), decltype(k)>())
std::cout << "j and k are of the same type." << std::endl;
else
std::cout << "j and k are of different type." << std::endl;
```
%% Cell type:code id: tags:
``` C++17
if (std::is_same<decltype(i), decltype(k)>())
std::cout << "i and k are of the same type." << std::endl;
else
std::cout << "i and k are of different type." << std::endl;
```
%% Cell type:markdown id: tags:
Despite the `auto k = j`, j and k don't share the same type! The reason for this is that `auto` loses information about pointers, reference or constness in the process...
A way to circumvent this is `auto& k = j`.
`decltype(auto)` was introduced to fill this hole: contrary to `auto` it retains all these informations:
%% Cell type:code id: tags:
``` C++17
#include <algorithm>
#include <iostream>
{
int i = 5;
int& j = i;
decltype(auto) k = j;
if (std::is_same<decltype(j), decltype(k)>())
std::cout << "j and k are of the same type." << std::endl;
else
std::cout << "j and k are of different type." << std::endl;
}
```
%% Cell type:markdown id: tags:
### `auto` and string litterals
**Beware:** when you declare a string litterals with `auto`, the type deduction makes it a `const char*`, not a `std::string`:
%% Cell type:code id: tags:
``` C++17
#include <algorithm>
#include <string>
#include <iostream>
auto hello_str = "Hello world"; // declares a char*
std::cout << "Is 'hello_str' a const char*? " << std::boolalpha << std::is_same<decltype(hello_str), const char*>() << std::endl;
std::cout << "Is 'hello_str' a std::string? " << std::boolalpha << std::is_same<decltype(hello_str), std::string>() << std::endl;
```
%% Cell type:markdown id: tags:
C++ 14 introduced a suffix to facilitate declaration of a `std::string` from a string litterals... but which requires to add a specific `using namespace` first (we will see that those are in a [much later notebook](../6-InRealEnvironment/5-Namespace.ipynb)).
%% Cell type:code id: tags:
``` C++17
#include <string>
using namespace std::string_literals;
std::string hello_string("Hello world"); // the 'classic' way to define a std::string
auto hello_str = "Hello world"s; // declares a std::string - requires first the using namespace directive
std::cout << "Is 'hello_string' a const char*? " << std::boolalpha << std::is_same<decltype(hello_string), const char*>() << std::endl;
std::cout << "Is 'hello_string' a std::string? " << std::boolalpha << std::is_same<decltype(hello_string), std::string>() << std::endl;
std::cout << "Is 'hello_str' a const char*? " << std::boolalpha << std::is_same<decltype(hello_str), const char*>() << std::endl;
std::cout << "Is 'hello_str' a std::string? " << std::boolalpha << std::is_same<decltype(hello_str), std::string>() << std::endl;
```
%% Cell type:markdown id: tags:
Not sure it it is entirely worth it (maybe when you define loads of `std::string` is a same file?) but you may see that syntax in an existing program.
%% Cell type:markdown id: tags:
© _CNRS 2016_ - _Inria 2018-2022_
_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)_
......
%% Cell type:markdown id: tags:
# [Getting started in C++](./) - [Procedural programming](./0-main.ipynb) - [Input and output streams](./6-Streams.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="#Predefined-streams" data-toc-modified-id="Predefined-streams-1">Predefined streams</a></span><ul class="toc-item"><li><span><a href="#std::cout" data-toc-modified-id="std::cout-1.1"><code>std::cout</code></a></span></li><li><span><a href="#std::cerr" data-toc-modified-id="std::cerr-1.2"><code>std::cerr</code></a></span></li><li><span><a href="#std:cin" data-toc-modified-id="std:cin-1.3"><code>std:cin</code></a></span></li></ul></li><li><span><a href="#Input/output-with-files" data-toc-modified-id="Input/output-with-files-2">Input/output with files</a></span><ul class="toc-item"><li><span><a href="#getline()" data-toc-modified-id="getline()-2.1"><code>getline()</code></a></span></li></ul></li><li><span><a href="#ostream-and-istream" data-toc-modified-id="ostream-and-istream-3"><code>ostream</code> and <code>istream</code></a></span></li><li><span><a href="#Conversion" data-toc-modified-id="Conversion-4">Conversion</a></span></li><li><span><a href="#Formatting-and-manipulators" data-toc-modified-id="Formatting-and-manipulators-5">Formatting and manipulators</a></span></li></ul></div>
%% Cell type:markdown id: tags:
## Predefined streams
The standard C++ includes an input/output library that specifies a common interface for all data exchanges with the outside world, based in particular on the insertion `<<` and extraction `>>` operators.
### `std::cout`
We have already dealt profusely with `std::cout` which provide the link to the Unix channel `stdout`:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
std::cout << "Hello world!" << std::endl;
}
```
%% Cell type:markdown id: tags:
### `std::cerr`
There is also `std::cerr`, which is related to Unix `stderr`:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
int n = -4;
if (n < 0)
std::cerr << "Positive or null value expected!" << std::endl;
}
```
%% Cell type:markdown id: tags:
### `std:cin`
And finally `std::cin`, related to Unix channel `stdin`. Line crossings are ignored (assimilated to spaces and tabs).
**WARNING** This works only with a recent version of Xeus-cling.
%% Cell type:code id: tags:
``` C++17
#include <random>
#include <iostream>
{
std::random_device rd; // Will be used to obtain a seed for the random number engine
std::mt19937 gen(rd()); // Standard mersenne_twister_engine seeded with rd()
std::uniform_int_distribution<> dis(0, 100);
auto hidden = dis(gen);
int guess = -1;
while (guess != hidden)
{
std::cout << "Find the value between 0 and 100: ";
std::cin >> guess;
if (guess > hidden)
std::cout << " Too high!" << std::endl;
else if (guess < hidden)
std::cout << " Too low!" << std::endl;
}
std::cout << "Congratulations! You have found the hidden number!" << std::endl;
}
```
%% Cell type:markdown id: tags:
`std::cin` is a bit more tricky to use than the others, as the risk the operation fails is really higher. For instance, if you give a string in the code above it will become crazy and keep printing the same message "Too high!" or "Too low!" (be ready to restart the kernel...). The following code fixes this:
%% Cell type:code id: tags:
``` C++17
#include <random>
#include <iostream>
{
std::random_device rd; //Will be used to obtain a seed for the random number engine
std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
std::uniform_int_distribution<> dis(0, 100);
auto hidden = dis(gen);
int guess = -1;
while (guess != hidden)
{
do
{
if (!std::cin)
{
std::cin.clear(); // clear the states of std::cin, putting it back to `goodbit`.
std::cin.ignore(10000, '\n'); // clean-up what might remain in std::cin before using it again.
}
std::cout << "Find the value between 0 and 100: ";
std::cin >> guess;
} while (!std::cin);
if (guess > hidden)
std::cout << " Too high!" << std::endl;
else if (guess < hidden)
std::cout << " Too low!" << std::endl;
}
std::cout << "Congratulations! You have found the hidden number!" << std::endl;
}
```
%% Cell type:markdown id: tags:
If you want to learn more about `std::cin`, you might want to look at [this post](https://stackoverflow.com/questions/5131647/why-would-we-call-cin-clear-and-cin-ignore-after-reading-input) on StackOverflow.
If you need to use it extensively, you should look more deeply the behaviour of the bit flags (`goodbit`, `badbit`, `failbit`, `eofbit`).
%% Cell type:markdown id: tags:
## Input/output with files
The same syntax with operators `<<` and `>>` may be used to interact with files; the streams are built with `std::ofstream` for an output stream and `std::ifstream` for an input stream.
%% Cell type:code id: tags:
``` C++17
#include <fstream> // for std::ifstream and std::ofstream
#include <iostream>
{
std::ofstream out("File.tmp");
out << 5 << std::endl;
out << -7 << std::endl;
out << 9 << std::endl;
out.close(); // file is written on disk when closed; automatically done when `out` gets out of scope otherwise
std::ifstream in("File.tmp");
int value;
while (in >> value)
std::cout << value << std::endl;
}
```
%% Cell type:markdown id: tags:
### `getline()`
When reading a file, if you want to interpret it line by line you should also consider `getline()`; this function may get a third argument to choose which separator to use (`\n` by default).
%% Cell type:code id: tags:
``` C++17
#include <iostream>
#include <fstream>
#include <string>
{
std::ifstream in("File.tmp"); // assumes previous cell has been played recently!
std::string line;
while (getline(in, line))
std::cout << line << std::endl;
}
```
%% Cell type:markdown id: tags:
## `ostream` and `istream`
If you want to devise a function that may take as argument either a `std::cout` or a `std::ofstream`, you should use a `std::ostream` (we'll study [later](../2-ObjectProgramming/6-inheritance.ipynb) why this works but just take my word for now):
%% Cell type:code id: tags:
``` C++17
#include <iostream>
void PrintOnStream(std::ostream& out)
{
out << "Printed on the chosen stream!" << std::endl;
}
```
%% Cell type:code id: tags:
``` C++17
PrintOnStream(std::cout);
```
%% Cell type:code id: tags:
``` C++17
#include <string>
#include <fstream>
{
std::ofstream out("test_stream.txt");
PrintOnStream(out);
}
```
%% Cell type:code id: tags:
``` C++17
{
// Read the content of the line previously written.
std::ifstream in("test_stream.txt");
std::string line;
getline(in, line);
std::cout << line << std::endl;
}
```
%% Cell type:markdown id: tags:
## Conversion
Stream syntax was until C++ 11 the only way to convert:
- A string into a number with `std::istringstream`
- A number into a string with `std::ostringstream`; the `str()` method returns the content as a `std::string`.
%% Cell type:code id: tags:
``` C++17
#include <sstream> // for std::ostringstream and std::istringstream
#include <string>
{
std::string number_as_string = "657";
int number;
std::istringstream iconv(number_as_string);
iconv >> number;
std::cout << "Number + 1 = " << number + 1 << std::endl;
}
```
%% Cell type:code id: tags:
``` C++17
#include <sstream> // for std::ostringstream and std::istringstream
#include <string>
{
int number = 657;
std::ostringstream oconv;
oconv << "The number is " << number;
std::cout << oconv.str() << std::endl;
}
```
%% Cell type:markdown id: tags:
To reuse a `std::ostringstream`, you must set its content to an empty string with an overloaded `str()`:
%% Cell type:code id: tags:
``` C++17
#include <sstream> // for std::ostringstream and std::istringstream
#include <string>
{
int number = 657;
std::ostringstream oconv;
oconv << "The number is " << number;
std::cout << oconv.str() << std::endl;
oconv.str(""); // reset oconv
oconv << "My new content is now there!";
std::cout << oconv.str() << std::endl;
}
```
%% Cell type:markdown id: tags:
Of course as for `std::cin` you may check the state of the object is still valid - if conversion is incorrect it won't!:
%% Cell type:code id: tags:
``` C++17
#include <sstream> // for std::ostringstream and std::istringstream
#include <string>
{
std::string number_as_string = "abd";
int number;
std::istringstream iconv(number_as_string);
iconv >> number; // invalid conversion!
if (!iconv)
std::cerr << "Invalid string!" << std::endl;
else
std::cout << "Number + 1 = " << number + 1 << std::endl;
}
```
%% Cell type:markdown id: tags:
In C++ 11, `std::to_string()` and the [`stoi` (and similar functions for long)](https://en.cppreference.com/w/cpp/string/basic_string/stol) were introduced to provide similar functionality with a more direct syntax:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
#include <string>
{
int number = 657;
std::string int_to_string("Number is ");
int_to_string += std::to_string(number);
std::cout << int_to_string << std::endl;
}
```
%% Cell type:code id: tags:
``` C++17
#include <iostream>
#include <string>
{
std::string number_as_string = "4557";
int number = std::stoi(number_as_string);
std::cout << "Number is " << number << std::endl;
}
```
%% Cell type:markdown id: tags:
It is however useful to be aware of the pre-C++ 11 syntax, especially for the number to string conversion: 'arithmetic' operations between strings (as `+`) incur copies that are avoided with the `std::ostringstream` syntax... but the construction of such a `std::ostringstream` object is costly as well...
C++ 20 should provide a better looking and more efficient syntax with `std::format` (see [this page](https://en.cppreference.com/w/cpp/utility/format/format) for more details).
C++ 20 should provide a better looking and more efficient syntax with `std::format` (see [this page](https://en.cppreference.com/w/cpp/utility/format/format) for more details)... but unfortunately current support by compilers is [not great](https://en.cppreference.com/w/cpp/compiler_support).
%% Cell type:markdown id: tags:
## Formatting and manipulators
You may act upon the exact formatting of the output.
I'll be honest: it's not what is the most refined tool in the C++ library, and you may long for the simplicity and power of something like Python (or even C `printf`, which is much simpler to use while being a mess under the hood...).
Once again `std::format` from C++ 20 should be a game changer here!
The difficulty is that some settings apply only to the next entry onto the stream (`width` here), while others change the behaviour permanently (until told otherwise of course, e.g. `precision` here). Here are few examples of these syntaxes:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
std::cout.setf(std::ios::showpos); // Add the `+` sign explicitly
std::cout.setf(std::ios::fixed, std::ios::floatfield); // use decimal notation only
std::cout.precision(2); // number of decimal digits
std::cout.width(8) ;
std::cout << 1.237 ;
std::cout.width(8) ;
std::cout << 100.1245 ;
std::cout.width(8) ;
std::cout << '\n' ;
std::cout.width(8) ;
std::cout << 1.5774e-2 ;
std::cout.width(8) ;
std::cout << 12. << '\n' ;
}
```
%% Cell type:markdown id: tags:
**Manipulators** provide a shorter syntax to add some of the properties as the `width` or the `precision`:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
std::cout.setf(std::ios::showpos); // Add the `+` sign explicitly
std::cout.setf(std::ios::fixed, std::ios::floatfield); // use decimal notation only
std::cout << std::setprecision(2) << std::setw(8) << 1.237;
std::cout << std::setw(8) << 100.1245;
std::cout << '\n';
std::cout << std::setw(8) << 1.5774e-2;
std::cout << std::setw(8) << 12. << '\n';
}
```
%% Cell type:markdown id: tags:
© _CNRS 2016_ - _Inria 2018-2022_
_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)_
......
%% Cell type:markdown id: tags:
# [Getting started in C++](/) - [Procedural programming](./0-main.ipynb) - [Static and constexpr](./7-StaticAndConstexpr.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="#Static-keyword" data-toc-modified-id="Static-keyword-1">Static keyword</a></span></li><li><span><a href="#Constexpr" data-toc-modified-id="Constexpr-2">Constexpr</a></span></li></ul></div>
%% Cell type:markdown id: tags:
## Static keyword
%% Cell type:markdown id: tags:
`static` may be seen as _at compile time_, whereas `dynamic` may be seen as _at runtime_.
A reply to this [StackOverflow question](https://stackoverflow.com/questions/572547/what-does-static-mean-in-c) gives a rather good summary of what `static` means in C (we shall see [later](../2-ObjectProgramming/5-static.ipynb) a C++-only keyword that unfortunately share the same moniker):
* Static defined local variables do not lose their value between function calls. In other words they are global variables, but scoped to the local function they are defined in.
* Static global variables are not visible outside of the C file they are defined in.
* Static functions are not visible outside of the C file they are defined in.
Only the first one is really relevant in C++, as for variables that should be accessible only in the current file C++ provides a concept of his own: the [**unnamed namespace**](../6-InRealEnvironment/5-Namespace.ipynb#Unnamed-namespace).
Let's see this first case in action:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
void FunctionWithStatic()
{
static int n = 0; // This initialisation occurs only at first call
// But `n` is not destroyed when the end bracket is reached and remains available
// in subsequent calls. However, `n` is available only inside this function.
std::cout << "The function has been called " << ++n << " times." << std::endl;
}
```
%% Cell type:code id: tags:
``` C++17
{
for (int i = 0; i < 5; ++i)
FunctionWithStatic();
}
```
%% Cell type:markdown id: tags:
It might be used for instance if you need to initialize something on the very first call of a function:
%% Cell type:code id: tags:
``` C++17
// Pseudo-code
void FunctionWithStatic()
{
static bool is_first_call = true;
if (is_first_call)
{
// Init stuff here on first call only, for instance something that requires heavy computation.
// ...
is_first_call = false;
}
// ... code executed at each call
}
```
%% Cell type:markdown id: tags:
## Constexpr
We've seen the allocation of an array on the stack follows this syntax:
%% Cell type:code id: tags:
``` C++17
int array[5ul];
```
%% Cell type:markdown id: tags:
where the size of the array is available at compile time (if not you have to use an array allocated on the heap at runtime).
Now imagine we want to init such an array with a size that results from a computation; let's say a Fibonacci series:
%% Cell type:code id: tags:
``` C++17
// Recursive function - Xeus cling may not appreciate if you call this cell several times.
auto fibonacci (std::size_t n)
auto Fibonacci (std::size_t n)
{
if (n == 0)
return 0;
if (n == 1)
return 1;
return fibonacci(n-1) + fibonacci(n-2);
return Fibonacci(n-1) + Fibonacci(n-2);
}
```
%% Cell type:code id: tags:
``` C++17
#include <iostream>
std::cout << fibonacci(5) << std::endl;
std::cout << fibonacci(10) << std::endl;
std::cout << Fibonacci(5) << std::endl;
std::cout << Fibonacci(10) << std::endl;
```
%% Cell type:code id: tags:
``` C++17
double array[fibonacci(5)]; // COMPILATION ERROR!
double array[Fibonacci(5)]; // COMPILATION ERROR!
```
%% Cell type:markdown id: tags:
This doesn't seem outlandish: a computation is involved and this computation happens at runtime - even if in fact all the required elements to perform it were available at compile time (there were for instance no argument read from command line involved).
In C++ 03, you had two choices to resolve this
- Using a macro...
- Or using [metaprogramming](../4-Templates/4-Metaprogramming.ipynb) which is a tad overkill... (and involves boilerplate code).
C++ introduced the keyword `constexpr`, which indicates something happens at compile time. It might be used for just a variable, or for more complicated construct such as functions or classes. The only requirement is that all the subparts used are themselves `constexpr`.
%% Cell type:code id: tags:
``` C++17
constexpr auto fibonacci_const (std::size_t n)
constexpr auto FibonacciConstexpr (std::size_t n)
{
if (n == 0)
return 0;
if (n == 1)
return 1;
return fibonacci_const(n-1) + fibonacci_const(n-2);
return FibonacciConstexpr(n-1) + FibonacciConstexpr(n-2);
}
```
%% Cell type:code id: tags:
``` C++17
double array[fibonacci_const(5)]; // Ok!
double array[FibonacciConstexpr(5)]; // Ok!
```
%% Cell type:markdown id: tags:
`constexpr` function may also be used as runtime function, but in this case their results can't of course be used at compile time.
%% Cell type:code id: tags:
``` C++17
#include <iostream>
int i = 7;
++i; // i is by no stretch a compile time variable!
std::cout << fibonacci_const(7) << std::endl;
std::cout << FibonacciConstexpr(i) << std::endl;
```
%% Cell type:markdown id: tags:
`constexpr` becomes increasingly powerful over time:
- The function `fibonacci_const` above does not in fact work before C++ 14: this version of the standard introduced the possibility to provide several `return` in a `constexpr` function.
- The function `FibonacciConstexpr` above does not in fact work before C++ 14: this version of the standard introduced the possibility to provide several `return` in a `constexpr` function.
- `constexpr` is added wherever possible in the STL. This is still a work in progress: if you look for instance the [CppReference page](https://en.cppreference.com/w/cpp/algorithm/count) for `std::count` algorithm, you will see this algorithm becomes `constexpr` in C++ 20.
We will see another use of `constexpr` in a [later notebook](../4-Templates/2-Specialization.ipynb#If-constexpr).
%% Cell type:markdown id: tags:
© _CNRS 2016_ - _Inria 2018-2022_
_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)_
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment