|
| 1 | +--- |
| 2 | +title: C++ Syntax |
| 3 | +--- |
| 4 | + |
| 5 | + |
| 6 | +# C++ Syntax |
| 7 | + |
| 8 | +Let's dive a litte more into C++ syntax by looking at how to declare variables, call functions, and make use of control structures. First of all though, we should take a quick look at _types_ in C++, since these are going to come up a lot. |
| 9 | + |
| 10 | +## Types |
| 11 | + |
| 12 | +We've already come across, `int`, `float`, `bool` and other keywords that define the *type* of variables and parameters. C++ is a *statically-typed* language, so the *types* of every single variable or function parameter must be known at *compile time*, and those types cannot change during runtime. |
| 13 | + |
| 14 | +> **Compile time** refers to the time at which the code is compiled. It's used in contrast to **runtime** (or **run time**) which refers to the time while the programming is actually running. For example, if a program requires a number to do some computation and you can write that number in the source code itself, that number is known at *compile time*. If, say, the user needs to input that number, the value is only known at *run time*. |
| 15 | +
|
| 16 | +This is in contrast to the *dynamically-typed* Python where we can define a variable `x` and assign it the integer value `2`, then reassign a string `"2"` to the same variable! |
| 17 | + |
| 18 | +```python |
| 19 | +x = 2 |
| 20 | +x = "2" |
| 21 | +``` |
| 22 | + |
| 23 | +In C++ this kind of code will produce a compiler error. |
| 24 | + |
| 25 | +We'll explore later what types are available in C++ (and how we can create our own) but a useful initial list of basic types is: |
| 26 | + |
| 27 | +- `bool`: a boolean value, i.e. `true` or `false` |
| 28 | +- `int`: an integer value, e.g. `-4, 0, 100` |
| 29 | +- `float`: a 32-bit floating-point value `-0.2, 0.0, 1.222, 2e-3` |
| 30 | +- `double`: a 64-bit floating-point value (same as `float` but can represent a greater range and precision of real numbers) |
| 31 | +- `char`: a single character, e.g. `'a', 'l', ';'` |
| 32 | + |
| 33 | +The _stardard library_ also defines many important types that we will use very frequently in C++; these are made available through `#include` statements and are prefixed by `std::` because they are part of the standard library _namespace_. Some common examples are given below including the necessary includes: |
| 34 | + |
| 35 | +- `std::size_t`: stands for "size type", and is machine dependent but will likely be 64 bits (8 bytes) on the machines you work on. It is unsigned (i.e. only represents values $\ge 0$) and is large enough to handle the largest size that an object can be in C++ on a given architecture; as such it is commonly used for indexing arrays and other data structures. |
| 36 | + - `#include<cstddef>` |
| 37 | +- `std::string`: text represented as a string of characters. Characters in a string can be iterated over similarly to lists. |
| 38 | + - `#include<string>` |
| 39 | +- `std::array<T, n>`: a kind of array with `n` elements of type `T`, e.g. `std::array<double, 3>` is an array of three doubles which could be used to represent a position in 3D space $(x, y, z)$. The size of the array must be known at compile time and cannot change. |
| 40 | + - `#include<array>` |
| 41 | +- `std::vector<T>`: a kind of array of elements of type `T`, e.g. `std::vector<int> {1, 100, -1}` declares a vector of integers. The size of a vector can be determined at runtime, and it can also grow and shrink as desired. |
| 42 | + - `#include<vector>` |
| 43 | + |
| 44 | +## Declaring and Assigning Variables |
| 45 | + |
| 46 | +A variable is declared by first declaring its _type_, then its _name_, and then (optionally) its _value_. A variable which is not assigned a value may be _uninitialised_ and contain an unknown value, so this should generally be avoided where possible. |
| 47 | + |
| 48 | +Some examples are given below; not the use of the `#include` and `std::` for the `string` type. |
| 49 | + |
| 50 | +```cpp |
| 51 | +#include<string> |
| 52 | + |
| 53 | +int main() |
| 54 | +{ |
| 55 | + int x = 5; |
| 56 | + double y = 7.2; |
| 57 | + std::string greeting = "Hello!"; |
| 58 | +} |
| 59 | +``` |
| 60 | + |
| 61 | +Some types in C++ can be _implicitly_ converted, but it is important to understand what is really happening here. Take for example, this code (we will omit the boilerplate for brevity): |
| 62 | + |
| 63 | +```cpp |
| 64 | +double y = 7.2; |
| 65 | +int x = 5; |
| 66 | + |
| 67 | +y = x; // What happens here? |
| 68 | +``` |
| 69 | + |
| 70 | +The assignment `y = x` takes the _value_ of `x` (an `int` representation of 5) and converts it to a `double` representation of the number 5, and then assigns that value to `y`. *The types of `x` and `y` have not changed.* `x` is still an integer `5` and `y` is now a `double` 5.0. |
| 71 | + |
| 72 | +This kind of implicit conversion happens quite commonly, take for example: |
| 73 | + |
| 74 | +```cpp |
| 75 | +double y = 7; |
| 76 | +``` |
| 77 | + |
| 78 | +Here the compiler will interpret the literal `7` as an `int` and then convert it to `double` to be assigned to the variable `y`. |
| 79 | + |
| 80 | +## Defining and Calling Functions |
| 81 | + |
| 82 | +We've already seen function signatures and the `main` function, let's look at how to define a simple function which will get used in our main. |
| 83 | + |
| 84 | +```cpp |
| 85 | +#include <iostream> |
| 86 | + |
| 87 | +// |
| 88 | +int square(int x) |
| 89 | +{ |
| 90 | + return x*x; |
| 91 | +} |
| 92 | + |
| 93 | +int main() |
| 94 | +{ |
| 95 | + int a = 5; |
| 96 | + int b = 7; |
| 97 | + |
| 98 | + int a_squared = square(a); |
| 99 | + |
| 100 | + std::cout << "a squared = " << a_squared << ", and b squared = " << square(b) << std::endl; |
| 101 | + |
| 102 | + return 0; |
| 103 | +} |
| 104 | +``` |
| 105 | +
|
| 106 | +We have put the definition of `square` _above_ `main` so that the compiler knows that this function exists before it tries to compile `main`. We will talk more about this in the section on writing C++ with multiple files. |
| 107 | +
|
| 108 | +The result of calling the function `square(a)` is assigned to the integer variable `a_squared`, whereas the result of `square(b)` is not assigned to a variable but is streamed directly to `cout`. |
| 109 | +
|
| 110 | +We can call functions without keeping the return value by just not assigning the function call: |
| 111 | +```cpp |
| 112 | +int main() |
| 113 | +{ |
| 114 | + int a = 5; |
| 115 | + square(a); |
| 116 | +} |
| 117 | +``` |
| 118 | +This would of course be a waste of time with a function like this, but is occasionally useful when a function is primarily used for its _side-effects_ and the returned information isn't important to you. |
| 119 | + |
| 120 | +## Conditional logic using `if`/`else` statements |
| 121 | + |
| 122 | +A true/false value is called a Boolean value, or `bool`. Conditional statements test the value of a Boolean value or expression and execute the following code block if it is `true`. (Remember that a code block is contained within curly braces `{}`, and can be as large as you like.) |
| 123 | + |
| 124 | +```cpp= |
| 125 | +// if statement with a Boolean variable |
| 126 | +if(condition) |
| 127 | +{ |
| 128 | + std::cout << "Condition was true!" << std::endl; |
| 129 | +} |
| 130 | +
|
| 131 | +// if statement with a Boolean expression |
| 132 | +if(x < 10) |
| 133 | +{ |
| 134 | + std::cout << "x is too small." << std::endl; |
| 135 | +} |
| 136 | +
|
| 137 | +// can also be a function which returns a bool! |
| 138 | +if(f(x)) |
| 139 | +{ |
| 140 | + std::cout << "f(x) was true!" << std::endl; |
| 141 | +} |
| 142 | +``` |
| 143 | +In the examples above, nothing will happen if the statement inside the brackets is not true. |
| 144 | + |
| 145 | +If you want something to happen when the statement is false, you can also use `else` and/or `else if` statements. |
| 146 | + |
| 147 | +```cpp= |
| 148 | +if(x < 10) |
| 149 | +{ |
| 150 | + std::cout << "x is small" << std::endl; |
| 151 | +} |
| 152 | +else if(x > 50) |
| 153 | +{ |
| 154 | + std::cout << "x is large" << std::endl; |
| 155 | +} |
| 156 | +else |
| 157 | +{ |
| 158 | + std::cout << "x is neither large nor small." << std::endl; |
| 159 | +} |
| 160 | +``` |
| 161 | + |
| 162 | +## Loops (`for` and `while`) |
| 163 | + |
| 164 | +```cpp= |
| 165 | +for(unsigned int i = 0; i < 100; ++i) |
| 166 | +{ |
| 167 | + // loop code goes here |
| 168 | +} |
| 169 | +``` |
| 170 | +- The brackets after the `for` set up three things: |
| 171 | + - first we declare a variable, if any, that we want to use for the loop. |
| 172 | + - next we have the loop condition; the loop continues while this is still true. |
| 173 | + - finally we have a statement which should execute at the end of each loop iteration. |
| 174 | + - In this case, we execute the loop 100 times, with `i` taking the values `0` to `99`. |
| 175 | + - The variable `i` is available inside the loop. |
| 176 | +- `unsigned int` is a type for _unsigned integers_, which are integers that cannot be negative. It's a good idea to use these for counting and other values which shouldn't be less than 0. You can also use `std::size_t`. |
| 177 | +- `++i` increments the value of `i` by 1. |
| 178 | + |
| 179 | +If we have a `vector` or similar container, we can loop over its elements without writing our own loop conditions: |
| 180 | +```cpp= |
| 181 | +#include <vector> |
| 182 | +
|
| 183 | +int main() |
| 184 | +{ |
| 185 | +
|
| 186 | + std::vector<int> v(10); // declare a vector of ints with 10 elements. |
| 187 | +
|
| 188 | + for(int &x : v) |
| 189 | + { |
| 190 | + std::cout << x << std::endl; |
| 191 | + } |
| 192 | + |
| 193 | +} |
| 194 | +``` |
| 195 | +- The `for` loop iterates over the elements of `v`. |
| 196 | +- At each iteration, the variable `x` is given the value of the current element. |
| 197 | +- This is a good way to iterate over containers when we don't need to refer to indices explicitly, as it avoids possible programmer errors! |
| 198 | + |
| 199 | +`while` loops have simpler syntax than `for` loops; they depend only on a condition, and the code block executes over and over until the condition is met. This is useful for situations where the number of iterations is not clear from the outset, for example running an iterative method until some convergence criterion is met. |
| 200 | + |
| 201 | +```cpp= |
| 202 | +while( (x_new - x_old) > 0.1) // convergence criterion |
| 203 | +{ |
| 204 | + x_old = x_new; |
| 205 | + x_new = f(x_old); //iteratively call function f on x until it converges |
| 206 | +} |
| 207 | +``` |
0 commit comments