Mentions légales du service

Skip to content
Snippets Groups Projects
Commit 2f90ae9d authored by STEFF Laurent's avatar STEFF Laurent
Browse files

comparison operators and not arithmetic operators

parent a6fe096a
Branches
No related tags found
No related merge requests found
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# [Getting started in C++](./) - [Procedural programming](./0-main.ipynb) - [Predefined types](./1-Variables.ipynb) # [Getting started in C++](./) - [Procedural programming](./0-main.ipynb) - [Predefined types](./1-Variables.ipynb)
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
<h1>Table of contents<span class="tocSkip"></span></h1> <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> <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: %% Cell type:markdown id: tags:
## Ordinary variables ## Ordinary variables
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### Declaration ### Declaration
%% Cell type:markdown id: tags: %% 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 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. the variable, followed by its name and a semicolon.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
{ {
int number; // integer variable int number; // integer variable
double real; // floating-point variable double real; // floating-point variable
std::cout << number << std::endl; std::cout << number << std::endl;
std::cout << real << std::endl; std::cout << real << std::endl;
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### Initialisation ### Initialisation
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Although not mandatory, it is **strongly** recommended to give an Although not mandatory, it is **strongly** recommended to give an
initial value to your variables, as an expression between brackets. 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 their 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 their computer!
If you give braces without values, a predefined and associated value If you give braces without values, a predefined and associated value
to the type is used (usually a form of 0). to the type is used (usually a form of 0).
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
{ {
int nb1 { 1 }; // integer variable set with the value 1 int nb1 { 1 }; // integer variable set with the value 1
int nb2 {}; // same as int nb2{0}; int nb2 {}; // same as int nb2{0};
double pi { 3.14 }; // real variable double pi { 3.14 }; // real variable
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
C++ actually supports many other historical forms C++ actually supports many other historical forms
of initialization, which you will encounter everywhere, including in this tutorial, of initialization, which you will encounter everywhere, including in this tutorial,
with brackets and/or an equal sign. There are some subtle differences with brackets and/or an equal sign. There are some subtle differences
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). 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: %% Cell type:code id: tags:
``` C++17 ``` C++17
{ {
int a = 5; int a = 5;
int b(5); int b(5);
int c { 5 }; // From C++ 11 onward int c { 5 }; // From C++ 11 onward
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
In all cases, even if there is an equal sign, it is important to remember 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 that it is an initialization, not an assignment (this will be
important when we will define our own types). important when we will define our own types).
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### Affectation ### Affectation
A new value is stored in an existing variable using the operator 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 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. on the right of the `=` sign is evaluated, and its result is assigned to the variable.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> // for std::cout and std::endl #include <iostream> // for std::cout and std::endl
{ {
int a {}, b {}, c {} ; // default initialization; set the values to 0 int a {}, b {}, c {} ; // default initialization; set the values to 0
std::cout << "Default initialization: a = " << a << ", b = " << b << " and c = " << c << std::endl; std::cout << "Default initialization: a = " << a << ", b = " << b << " and c = " << c << std::endl;
a = 4; a = 4;
b = 7; b = 7;
c = a + b; c = a + b;
std::cout << "After affectations: a = " << a << ", b = " << b << " and c = " << c << std::endl; std::cout << "After affectations: a = " << a << ", b = " << b << " and c = " << c << std::endl;
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Affectations may be chained: Affectations may be chained:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
{ {
int a {}, b {}, c {}; int a {}, b {}, c {};
a = b = c = 5; a = b = c = 5;
std::cout << "a = " << a << ", b = " << b << " and c = " << c << std::endl; std::cout << "a = " << a << ", b = " << b << " and c = " << c << std::endl;
} }
``` ```
%% Cell type:markdown id: tags: %% 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: 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: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
{ {
int a {}, b {}, c {} ; // default initialization; set the values to 0 int a {}, b {}, c {} ; // default initialization; set the values to 0
a += 4; // add 4 to the current value of a a += 4; // add 4 to the current value of a
std::cout << "a = " << a << std::endl; std::cout << "a = " << a << std::endl;
a *= 7; // multiply current value of a by 7 a *= 7; // multiply current value of a by 7
std::cout << "a = " << a << std::endl; std::cout << "a = " << a << std::endl;
a /= 5; // divide a by 5 and assign the quotient to a a /= 5; // divide a by 5 and assign the quotient to a
std::cout << "a = " << a << std::endl; std::cout << "a = " << a << std::endl;
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### Scope and blocks ### Scope and blocks
A variable is destroyed when it becomes **out of scope**. 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. 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 `{}`. A **block** is essentially what is between braces `{}`.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
int a = 5; int a = 5;
{ {
std::cout << "a is available at this level: " << a << std::endl; std::cout << "a is available at this level: " << a << std::endl;
{ {
int b = 10; int b = 10;
std::cout << "b is available at this level: " << b << std::endl; std::cout << "b is available at this level: " << b << std::endl;
std::cout << "and a is also still available: " << a << std::endl; std::cout << "and a is also still available: " << a << std::endl;
} // b becomes out of scope } // b becomes out of scope
std::cout << "a is available at this level: " << a << "... but b is not!" << std::endl; std::cout << "a is available at this level: " << a << "... but b is not!" << std::endl;
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Within a same block, a variable name may be only used once: Within a same block, a variable name may be only used once:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
{ {
int a = 5; int a = 5;
std::cout << "Value = " << a << std::endl; std::cout << "Value = " << a << std::endl;
int a = 1; // COMPILATION ERROR int a = 1; // COMPILATION ERROR
} }
``` ```
%% Cell type:markdown id: tags: %% 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): **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: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
int a = 5; int a = 5;
{ {
int a = 10; int a = 10;
std::cout << "a is available at this level; it is the most inner scope one: " << a << std::endl; 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; std::cout << "Of course same here: " << a << std::endl;
} }
a = a + 5; // this is the inner most 'a' that is modified a = a + 5; // this is the inner most 'a' that is modified
} // inner most a becomes out if scope } // 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'" 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; "is the initial one: " << a << std::endl;
``` ```
%% Cell type:markdown id: tags: %% 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. 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: %% Cell type:markdown id: tags:
### Increment and decrement operators ### 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. 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 before the variable, it is a **pre-increment**.
* If the sign is placed after the variable, it is a **post-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: An example is the better way to explain the difference between both:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
int a = 5; int a = 5;
int b = 3; int b = 3;
a++; // increment a by 1. a++; // increment a by 1.
++a; // same, both are actually equivalents here. ++a; // same, both are actually equivalents here.
int c = a + b; int c = a + b;
std::cout << "a = " << a << ", b = " << b << " and c = " << c << std::endl; std::cout << "a = " << a << ", b = " << b << " and c = " << c << std::endl;
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
c = a-- + b; // first a + b is evaluated, and only then a is decremented. 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; std::cout << "a = " << a << ", b = " << b << " and c = " << c << std::endl;
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
c = a + ++b; // first b is incremented, and only then a + b is evaluated. 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; std::cout << "a = " << a << ", b = " << b << " and c = " << c << std::endl;
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Honestly it's usually better to remove any ambiguity by separating explicitly both operations: Honestly it's usually better to remove any ambiguity by separating explicitly both operations:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
{ {
int a = 7; int a = 7;
int b = 3; int b = 3;
int c = a + b; int c = a + b;
std::cout << "a = " << a << ", b = " << b << " and c = " << c << std::endl; std::cout << "a = " << a << ", b = " << b << " and c = " << c << std::endl;
c = a + b; c = a + b;
--a; // equivalent to a-- but for reasons related to the standard library I advise you --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. // to rather use the pre-increment form when both are equivalent.
std::cout << "a = " << a << ", b = " << b << " and c = " << c << std::endl; std::cout << "a = " << a << ", b = " << b << " and c = " << c << std::endl;
++b; // same: equivalent to b++; ++b; // same: equivalent to b++;
c = a + b; c = a + b;
std::cout << "a = " << a << ", b = " << b << " and c = " << c << std::endl; std::cout << "a = " << a << ", b = " << b << " and c = " << c << std::endl;
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### Comparing values ### Comparing values
As shown above, `=` is the affectation operator. To compare two values, the symbol to use is `==`. As shown above, `=` is the affectation operator. To compare two values, the symbol to use is `==`.
Other arithmetic operators are: Other comparison operators are:
| Operator | Effect | | Operator | Effect |
|:------------- |:-------------------------------------------:| |:------------- |:-------------------------------------------:|
| `a == b` | `true` if a and b are equals | | `a == b` | `true` if a and b are equals |
| `a != b` | `true` if a and b are different | | `a != b` | `true` if a and b are different |
| `a < b` | `true` if a is less than b | | `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 b |
| `a >= b` | `true` if a is greater than or equal to 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 | | `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)). 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: %% Cell type:markdown id: tags:
## References ## References
A reference is a variable that acts as a kind of alias, and provides another name for the same variable. 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 When defining a reference, it must be **immediately** initialized by
indicating to which variable it should point; it cannot be changed after that. indicating to which variable it should point; it cannot be changed after that.
The syntax is to add a `&` character just after the type: The syntax is to add a `&` character just after the type:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
int a { 2 }; int a { 2 };
int b { a }; int b { a };
int& c { a }; // c is a reference to a int& c { a }; // c is a reference to a
std::cout << "Initial values : a = " << a << ", b = " << b << " and c = " << c << std::endl; std::cout << "Initial values : a = " << a << ", b = " << b << " and c = " << c << std::endl;
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
a = -7; a = -7;
std::cout << "Modify a : a = " << a << ", b = " << b << " and c = " << c << std::endl; std::cout << "Modify a : a = " << a << ", b = " << b << " and c = " << c << std::endl;
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
b = 42; b = 42;
std::cout << "Modify b : a = " << a << ", b = " << b << " and c = " << c << std::endl; std::cout << "Modify b : a = " << a << ", b = " << b << " and c = " << c << std::endl;
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
c = 0; c = 0;
std::cout << "Modify c : a = " << a << ", b = " << b << " and c = " << c << std::endl; std::cout << "Modify c : a = " << a << ", b = " << b << " and c = " << c << std::endl;
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Reference is a purely C++ concept that doesn't exist in C. Reference is a purely C++ concept that doesn't exist in C.
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Pointers ## Pointers
A pointer contains the address in memory 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 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, of another variable. To explicitly extract the address of this other variable,
we use the symbol `&`. we use the symbol `&`.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
int a { 2 }; int a { 2 };
int* p {&a}; // define a pointer p which is initialized with the address of a int* p {&a}; // define a pointer p which is initialized with the address of a
std::cout << "a = " << a << std::endl; std::cout << "a = " << a << std::endl;
std::cout << "p = " << p << std::endl; std::cout << "p = " << p << std::endl;
``` ```
%% Cell type:markdown id: tags: %% 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...) 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: %% Cell type:code id: tags:
``` C++17 ``` C++17
std::cout << "Value stored at p = " << *p << std::endl; std::cout << "Value stored at p = " << *p << std::endl;
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
The `*` syntax may be used to modify the underlying value: The `*` syntax may be used to modify the underlying value:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
*p = *p + 5; *p = *p + 5;
std::cout << "After the operation: pointer " << p << " stores the value " << *p << std::endl; std::cout << "After the operation: pointer " << p << " stores the value " << *p << std::endl;
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Pointers may be used as variables by themselves and see their actual content changed during execution of a program: Pointers may be used as variables by themselves and see their actual content changed during execution of a program:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
int b { 3 }; int b { 3 };
p = &b; p = &b;
std::cout << "After the pointer assignation: pointer " << p << " stores the value " << *p << std::endl; 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; std::cout << "(and value of a remains unchanged: " << a << ')' << std::endl;
``` ```
%% Cell type:markdown id: tags: %% 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). 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: %% Cell type:markdown id: tags:
### Cheatsheet: pointers and reference syntax ### Cheatsheet: pointers and reference syntax
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
| Applied to: | A type `T` | A variable `x` | 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 | | * | 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) | | & | Reference to an object of type `T` | Address of the variable (i.e. a pointer) |
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### Chaining pointers ### Chaining pointers
It is possible to chain pointers: It is possible to chain pointers:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
{ {
int n = 5; int n = 5;
int* p = &n; int* p = &n;
int** q = &p; 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 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; std::cout << "**q gives the original `n` value: " << **q << std::endl;
} }
``` ```
%% Cell type:markdown id: tags: %% 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). 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: %% Cell type:markdown id: tags:
### `nullptr` ### `nullptr`
A pointer can also designate no variables if initialized with the special value 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). `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. 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. 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: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
{ {
int* p = nullptr; // define a null pointer p int* p = nullptr; // define a null pointer p
std::cout << "\t p: address = " << p << ", value = " << *p << std::endl; // Dereferencing p is misguided! std::cout << "\t p: address = " << p << ", value = " << *p << std::endl; // Dereferencing p is misguided!
} }
``` ```
%% Cell type:markdown id: tags: %% 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!) `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: %% Cell type:markdown id: tags:
## Constant variables and pointers ## 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: 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: %% Cell type:code id: tags:
``` C++17 ``` C++17
const double pi = 3.1415927; 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, 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. // so that is what you will see in the remaining of the lecture.
// There are however very persuasive arguments for the other convention: // There are however very persuasive arguments for the other convention:
// see http://slashslash.info/eastconst/ // see http://slashslash.info/eastconst/
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
pi = 5.; // COMPILATION ERROR! pi = 5.; // COMPILATION ERROR!
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
pi_2 = 7.; // COMPILATION ERROR! pi_2 = 7.; // COMPILATION ERROR!
``` ```
%% Cell type:markdown id: tags: %% 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): 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: %% Cell type:code id: tags:
``` C++17 ``` C++17
{ {
int a { 2 }, b { 3 }; int a { 2 }, b { 3 };
int* p { &a }; // Both pointer and pointed values are modifiable. int* p { &a }; // Both pointer and pointed values are modifiable.
p = &b; // OK p = &b; // OK
*p = 5; // OK *p = 5; // OK
} }
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
int a { 2 }, b { 3 }; int a { 2 }, b { 3 };
int* const p { &a }; // Value is modifiable, but not the address pointed to. int* const p { &a }; // Value is modifiable, but not the address pointed to.
*p = 5; // OK *p = 5; // OK
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
p = &b; // COMPILATION ERROR p = &b; // COMPILATION ERROR
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
const int* p { &a }; // Address pointed to is modifiable, but not the underlying value. const int* p { &a }; // Address pointed to is modifiable, but not the underlying value.
p = &b; // OK p = &b; // OK
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
*p = 5; // COMPILATION ERROR *p = 5; // COMPILATION ERROR
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
int a { 2 }, b { 3 }; int a { 2 }, b { 3 };
const int* const p { &a }; // Nothing is modifiable const int* const p { &a }; // Nothing is modifiable
p = &b; // COMPILATION ERROR p = &b; // COMPILATION ERROR
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
*p = 5; // COMPILATION ERROR *p = 5; // COMPILATION ERROR
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
**IMPORTANT**: Even if declared `const`, the pointed value is **IMPORTANT**: Even if declared `const`, the pointed value is
not intrinsically constant. It just can't be not intrinsically constant. It just can't be
modified through this precise pointer. modified through this precise pointer.
If other variables reference the same memory area and If other variables reference the same memory area and
are not constant, they are able to modify the value: are not constant, they are able to modify the value:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
{ {
int a { 2 }, b { 3 }; int a { 2 }, b { 3 };
const int* p { &a }; // Address pointed to is modifiable, but not the underlying value. const int* p { &a }; // Address pointed to is modifiable, but not the underlying value.
std::cout << "Value pointed by pointer 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. int* p2 {&a}; // Pointer to the same memory area, but no constness here.
*p2 = 10; *p2 = 10;
std::cout << "Value pointed by pointer 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; a = -3;
std::cout << "Value pointed by pointer p (and modified through variable directly) is: " << *p << std::endl; std::cout << "Value pointed by pointer p (and modified through variable directly) is: " << *p << std::endl;
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
On the other hand, pointers can't be used as a work-around to modify a constant value: On the other hand, pointers can't be used as a work-around to modify a constant value:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
const int n { 3 }; const int n { 3 };
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
int* p { &n }; // COMPILATION ERROR int* p { &n }; // COMPILATION ERROR
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
const int* p_n2 { &n }; // OK const int* p_n2 { &n }; // OK
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Arrays ## Arrays
The operator `[]` enables the creation of an array. The operator `[]` enables the creation of an array.
**Beware:** In C++, the indexes of an array start at 0. **Beware:** In C++, the indexes of an array start at 0.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
{ {
int i[10] ; // Array of 10 integers - not initialised properly! int i[10] ; // Array of 10 integers - not initialised properly!
double x[3] = { 1., 2., 3. } ; // Array of 3 reals, C++ 11 syntax 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[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 << "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 ; std::cout << "x[1] = " << x[1] << " (expected: 2.)" << std::endl ;
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Multi-dimensional arrays are also possible: Multi-dimensional arrays are also possible:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #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[0][0] = " << k[0][0] << " (expected: 5)" << std::endl;
std::cout << "k[1][2] = " << k[1][2] << " (expected: 9)" << 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. 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[0][0] = " << l[0][0] << " (expected: 0)" << std::endl;
std::cout << "l[1][2] = " << l[1][2] << " (expected: 0)" << std::endl; std::cout << "l[1][2] = " << l[1][2] << " (expected: 0)" << std::endl;
} }
``` ```
%% Cell type:markdown id: tags: %% 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). **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: %% Cell type:markdown id: tags:
### Arrays and pointers ### 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. 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: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
int i[2] = { 10, 20 } ; int i[2] = { 10, 20 } ;
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
std::cout << *i << " (expected: 10)" << std::endl; std::cout << *i << " (expected: 10)" << std::endl;
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
std::cout << *i + 1 << " (expected: 11)" << std::endl; std::cout << *i + 1 << " (expected: 11)" << std::endl;
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
std::cout << *(i + 1) << " (expected: 20)" << std::endl; std::cout << *(i + 1) << " (expected: 20)" << std::endl;
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
int* j = i; // OK! int* j = i; // OK!
++j; // OK! ++j; // OK!
std::cout << "Value pointed by j is " << *j << " (expected: 20)" << std::endl; std::cout << "Value pointed by j is " << *j << " (expected: 20)" << std::endl;
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
[© Copyright](../COPYRIGHT.md) [© Copyright](../COPYRIGHT.md)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment