Commit 20ec151a authored by GILLES Sebastien's avatar GILLES Sebastien
Browse files

Add two notebooks.

parent 8301d87a
.ipynb_checkpoints
*/.ipynb_checkpoints
Untitled.ipynb
\ No newline at end of file
Untitled.ipynb
1-ProceduralProgramming/File.tmp
\ No newline at end of file
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# [Getting started in C++](/) - [Procedural programming](/notebooks/1-ProceduralProgramming/0-main.ipynb) - [Dynamic allocations](/notebooks/1-ProceduralProgramming/6-DynamicAllocation.ipynb)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Introduction\n",
"\n",
"In C++, we can finely control the life cycle of objects and manage the memory allocated to them. This is what makes it possible to create more powerful applications than with other languages, but it is also the main source of errors in the language. Pointers and dynamic memory management: watch out for danger!\n",
"\n",
"## Stack\n",
"\n",
"The ordinary variables of C++ have a lifetime limited to the current instruction block, whether it is the current function, or a instruction block attached to an `if`, `for` or just independant.\n",
"\n",
"The memory allocated to them is located in an area called a **stack**, and is automatically relieved when exiting the current block using the **last in, first out** principle.\n",
"\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"{\n",
" {\n",
" int a { 5 };\n",
" double b { 7.4 };\n",
" } // at the end of this block, b is released first and then a - but 99.99 % of the time you shouldn't care\n",
" // about that order!\n",
"\n",
" // a and b are not available here\n",
" \n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"There are few limitations with the stack:\n",
"\n",
"* The number of memory you can allocate on the stack is rather limited. On a current POSIX OS the order of magnitude is ~ 8 Mo (on Unix type `ulimit -a` in a terminal to get this information). If you allocate more you will get a **stack overflow** (and now you know why the [most popular developers forum](https://stackoverflow.com/) is named this way!)\n",
"* The information is very local; you can't use it elsewhere. If you pass the variable as argument in a function for instance a copy is made (or if you're using a reference or a pointer you must be sure all is done when the block is exited!)\n",
"* Stack information must be known at compile time: if you're allocating an array on the stack you must know its size beforehand!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Heap and free store\n",
"\n",
"You can in fact also explicitly place a variable in another memory area called **heap** or **free store**; doing so overcomes the stack limitations mentioned above.\n",
"\n",
"This is done by calling the `new` operator, who reserves the memory and returns its address, so that the user can store it _in a pointer_.\n",
"\n",
"The **heap** is independent of the **stack** and the variable thus created exists as long as the `delete` operator is not explicitly called. The creation and destruction of this type of variable is the responsibility of the programmer. \n",
"\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"5\n"
]
}
],
"source": [
"#include <iostream>\n",
"\n",
"{\n",
" int* n = new int(5); // variable created on the heap.\n",
" \n",
" std::cout << *n << std::endl;\n",
" \n",
" delete n; // deletion must be explicitly called; if not there is a memory leak!\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"What is especially tricky is that:\n",
"\n",
"* Creating and destroying can be done in places very disconnected in your program.\n",
"* You must ensure that whatever the runtime path used in your program each variable allocated on the heap:\n",
" - is destroyed (otherwise you get a **memory leak**)\n",
" - is only destroyed once (or your program will likely crash with a message about **double deletion**).\n",
" \n",
"In sophisticated programs, this could lead in serious and tedious bookkeeping to ensure all variables are properly handled, even if tools such as [Valgrind](http://www.valgrind.org/) or [Address sanitizer](https://github.com/google/sanitizers/wiki/AddressSanitizer) may help to find out those you will probably have forgotten somewhere along the way.\n",
"\n",
"To be honest, C++ gets quite a bad name due to this tedious memory handling; fortunately the RAII idiom provides a neat way to automatize nicely memory management (which we'll study [later](/notebooks/5-UsefulConceptsAndSTL/2-RAII.ipynb)) and some vocal critics on forums that regret the lack of [garbage collection](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)) might actually not be aware of this fundamental (from my point of view at least) idiom."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Free store?\n",
"\n",
"**Free store** is very similar in functionality to the **heap** (to the point I had to [check the difference](https://stackoverflow.com/questions/1350819/c-free-store-vs-heap) before writing this...) , and more often than not one word might be used as the other. If you want to be pedantic:\n",
"\n",
"* When memory is handled by `new`/`delete`, you should talk about **free store**.\n",
"* When memory is handled by `malloc`/`free` (the C functions), you should talk about **heap**.\n",
"\n",
"Pedantry aside, the important thing to know is to never mix both syntax: if you allocate memory by `new` don't use `free` to relieve it.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Arrays on heap\n",
"\n",
"If you want to init an array which size you do not know at compile time or that might overflow the stack, you may to do with `new` syntax mixed with `[]`:\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"{\n",
" int* pi_first_five_digits = new int[5];\n",
" \n",
" pi_first_five_digits[0] = 3;\n",
" pi_first_five_digits[1] = 1;\n",
" pi_first_five_digits[2] = 4;\n",
" pi_first_five_digits[3] = 1;\n",
" pi_first_five_digits[4] = 5;\n",
" \n",
" delete[] pi_first_five_digits;\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Please notice that:\n",
"\n",
"* No value can be assigned in construction: you must first allocate the memory for the array and only in a second time fill it.\n",
"* A `[]` **must** be added to indicate to the compiler this is actually an array that is destroyed.\n",
"\n",
"In fact, my advice would be to avoid entirely to deal directly with such arrays and use containers from the standard library such as `std::vector`:\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"#include <vector>\n",
"\n",
"{\n",
" std::vector<int> pi_first_five_digits { 3, 1, 4, 1, 5 };\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"that does the exact same job in a shorter way and is much more secure to use.\n",
"\n",
"We shall see `std::vector` more deeply [later](/notebooks/5-UsefulConceptsAndSTL/3-Containers.ipynb) but will nonetheless use it before this as it is a rather elementary brick in most C++ codes."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"© _CNRS 2016_ - _Inria 2018_ \n",
"_This notebook is an adaptation of a lecture prepared and redacted 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/)_ \n",
"_The present version has been redacted by Sébastien Gilles and Vincent Rouvreau (Inria)_\n",
"\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "C++17",
"language": "C++17",
"name": "xeus-cling-cpp17"
},
"language_info": {
"codemirror_mode": "text/x-c++src",
"file_extension": ".cpp",
"mimetype": "text/x-c++src",
"name": "c++",
"version": "-std=c++17"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# [Getting started in C++](/) - [Procedural programming](/notebooks/1-ProceduralProgramming/0-main.ipynb) - [Input and output streams](/notebooks/1-ProceduralProgramming/7-Streams.ipynb)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Predefined streams\n",
"\n",
"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.\n",
"\n",
"### `std::cout`\n",
"\n",
"We have already dealt profusely with `std::cout` which provide the link to the Unix channel `stdout`:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Hello world!\n"
]
}
],
"source": [
"#include <iostream>\n",
"\n",
"{\n",
" std::cout << \"Hello world!\" << std::endl;\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### `std::cerr`\n",
"\n",
"There is also `std::cerr`, which is related to Unix `stderr`:"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"Positive or null value expected!\n"
]
}
],
"source": [
"#include <iostream>\n",
"\n",
"{\n",
" int n = -4;\n",
" \n",
" if (n < 0)\n",
" std::cerr << \"Positive or null value expected!\" << std::endl;\n",
" \n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### `std:cin`\n",
"\n",
"And finally `std::cin`, related to Unix channel `stdin`. Line crossings are ignored (assimilated to spaces and tabs).\n",
" \n",
"**WARNING** Due to the very specific nature of standard input, this is a feature that does not work either in a Xeus-cling notebook or in [Coliru](https://coliru.stacked-crooked.com/) or [Wandbox](https://wandbox.org/). You will have to trust us or try with the compiler on your computer..."
]
},
{
"cell_type": "raw",
"metadata": {},
"source": [
"#include <random>\n",
"#include <iostream>\n",
"\n",
"\n",
"int main()\n",
"{\n",
" std::random_device rd; // Will be used to obtain a seed for the random number engine\n",
" std::mt19937 gen(rd()); // Standard mersenne_twister_engine seeded with rd()\n",
" std::uniform_int_distribution<> dis(0, 100);\n",
" \n",
" auto hidden = dis(gen);\n",
" \n",
" int guess = -1;\n",
" \n",
" while (guess != hidden)\n",
" {\n",
" std::cout << \"Find the value between 0 and 100: \";\n",
" \n",
" std::cin >> guess;\n",
" \n",
" if (guess > hidden)\n",
" std::cout << \" Too high!\" << std::endl;\n",
" else if (guess < hidden)\n",
" std::cout << \" Too low!\" << std::endl;\n",
" }\n",
" \n",
" std::cout << \"Congratulations! You have found the hidden number!\" << std::endl;\n",
"\n",
" return EXIT_SUCCESS;\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`std::cin` is a bit more tricky to use that the others, as the risk the operation fails is really higher. For instance, if you give a string here the code will be crazy and keep printing the same message \"Too high!\" or \"Too low!\". The following code fixes this:"
]
},
{
"cell_type": "raw",
"metadata": {},
"source": [
"#include <random>\n",
"#include <iostream>\n",
"\n",
"\n",
"int main()\n",
"{\n",
" std::random_device rd; //Will be used to obtain a seed for the random number engine\n",
" std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()\n",
" std::uniform_int_distribution<> dis(0, 100);\n",
" \n",
" auto hidden = dis(gen);\n",
" \n",
" int guess = -1;\n",
" \n",
" while (guess != hidden)\n",
" {\n",
" do\n",
" {\n",
" if (!std::cin)\n",
" {\n",
" std::cin.clear(); // clear the states of std::cin, putting it back to `goodbit`.\n",
" std::cin.ignore(10000, '\\n'); // clean-up what might remain in std::cin before using it again.\n",
" }\n",
" \n",
" std::cout << \"Find the value between 0 and 100: \"; \n",
" std::cin >> guess; \n",
"\n",
" } while (!std::cin);\n",
" \n",
" \n",
" if (guess > hidden)\n",
" std::cout << \" Too high!\" << std::endl;\n",
" \n",
" else if (guess < hidden)\n",
" std::cout << \" Too low!\" << std::endl;\n",
" \n",
" }\n",
" \n",
" std::cout << \"Congratulations! You have found the hidden number!\" << std::endl;\n",
"\n",
" return EXIT_SUCCESS;\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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.\n",
"\n",
"If you need to use it extensively, you should look more deeply the behaviour of the bit flags (`goodbit`, `badbit`, `failbit`, `eofbit`).\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Input/output with files\n",
"\n",
"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.\n",
"\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"5\n",
"-7\n",
"9\n"
]
}
],
"source": [
"#include <fstream> // for std::ifstream and std::ofstream\n",
"#include <iostream>\n",
"\n",
"{\n",
" std::ofstream out(\"File.tmp\");\n",
" \n",
" out << 5 << std::endl;\n",
" out << -7 << std::endl;\n",
" out << 9 << std::endl; \n",
" \n",
" out.close(); // file is written on disk when closed; automatically done when `out` gets out of scope otherwise\n",
" \n",
" std::ifstream in(\"File.tmp\");\n",
" \n",
" int value;\n",
" \n",
" while (in >> value)\n",
" std::cout << value << std::endl;\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### `getline()`\n",
"\n",
"When reading a file, if you want to interpret it line by line you should also consider `getline()`; this function may get a thrid argument to choose which separator to use (`\\n` by default)."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"5\n",
"-7\n",
"9\n"
]
}
],
"source": [
"#include <iostream>\n",
"#include <fstream>\n",
"#include <string>\n",
"\n",
"{ \n",
" std::ifstream in(\"File.tmp\");\n",
" \n",
" std::string line;\n",
" \n",
" while (getline(in, line))\n",
" std::cout << line << std::endl;\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Conversion\n",
"\n",
"Stream syntax was until C++ 11 the only way to convert:\n",
"\n",
"- A string into a number with `std::istringstream`\n",
"- A number into a string with `std::ostringstream`; the `str()` method returns the content as a `std::string`."
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Number = 657\n"
]
}
],
"source": [
"#include <sstream> // for std::ostringstream and std::istringstream\n",
"#include <string>\n",
"\n",
"{\n",
" std::string number_as_string = \"657\";\n",
" \n",
" int number;\n",
" \n",
" std::istringstream iconv(number_as_string);\n",
" iconv >> number;\n",
" \n",
" std::cout << \"Number = \" << number << std::endl;\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The number is 657\n"
]
}
],
"source": [
"#include <sstream> // for std::ostringstream and std::istringstream\n",
"#include <string>\n",
"\n",
"{\n",
" int number = 657;\n",
" \n",
" std::ostringstream oconv; \n",
" oconv << \"The number is \" << number; \n",
" std::cout << oconv.str() << std::endl;\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To reuse a `std::ostringstream`, you must set its content to an empty string with an overloaded `str()`:"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The number is 657\n",
"My new content is now there!\n"
]
}
],
"source": [
"#include <sstream> // for std::ostringstream and std::istringstream\n",
"#include <string>\n",
"\n",
"{\n",
" int number = 657;\n",
" \n",
" std::ostringstream oconv; \n",
" oconv << \"The number is \" << number; \n",
" std::cout << oconv.str() << std::endl;\n",
" \n",
" oconv.str(\"\"); // reset oconv\n",
" oconv << \"My new content is now there!\";\n",
" std::cout << oconv.str() << std::endl; \n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Formatting and manipulators\n",
"\n",
"You may act upon the exact formatting of the output\n",
"\n",
"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!).\n",
"\n",
"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:\n",
"\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [
{
"name": "stdout",