# ECE 150: C++ ## Non-decimal numbers Binary numbers are prefixed with `0b`. !!! example The following two snippets are equivalent: ```cpp int a{0b110001}; ``` ```cpp int a{25}; ``` To convert from **binary to decimal**, each digit should be treated as a power of two much like in the base 10 system. !!! example $$ \text{0b1011}=1\times2^3 + 0\times2^2+1\times2^1+1\times2^0=11 $$ Binary addition is the same as decimal addition except $1+1=10$ and $1+1+1=11$. To convert from **decimal to binary**, the number should be repeatedly divided by 2 and the binary number taken from the remainders from bottom to top. !!! example $$ \begin{align*} 13 &= 2\times6 + 1 \\ 6 &= 2\times3 + 0 \\ 3 &= 2\times1 + 1 \\ 1 &= 2\times0 + 1 \\ &\therefore 13 = \text{0b1101} \end{align*} $$ To convert from **binary to hexadecimal**, each group of four digits beginning from the right should be converted to their hexadecimal representation. To convert from **hexadecimal to binary**, each hexadecimal digit should be expanded into its four-digit binary representation. To convert from **decimal to hexadecimal**, the number should be repeatedly divided by 16 and the hex number taken from the remainders from bottom to top. !!! example $$ \begin{align*} 37 &= 16\times2 + 5 \\ 2 &= 16\times0 + 2 \\ &\therefore 37 = \text{0x25} \end{align*} $$ ## Numbers ### Integers !!! definition - A **carry** occurs if an overflow or underflow happens in an unsigned number. The $k$th bit of a number is as known as its **coefficient** because it can be expressed in the form $n\times 2^k$ in binary or $n\times 16^k$ in hexadecimal. | Type | Bits | Can store | | --- | --- | --- | | `short` | 16 | $\pm2^{15}-1$ | | `int` | 32 | $\pm2^{31}-1$ | | `long` | 64 | $\pm2^{63}-1$ | | `char` | 8 | N/A | | `unsigned short` | 16 | $2^{16}-1$ | | `unsigned int` | 32 | $2^{32}-1$ | | `unsigned long` | 64 | $2^{64}-1$ | | `unsigned char` | 8 | N/A | The `sizeof()` operator evaluates the size the type takes in memory at compile time. Signed numbers use the first bit to represent positive or negative numbers. A negative number is equal to the **two's complement** of its positive form. This allows subtraction to be done by taking the two's complement of the subtracter. !!! definition The two's complement form of a number flips all bits **but the rightmost digit equal to one**. ### Floating point numbers | Type | Bits | Digits of precision | | --- | --- | --- | | `float` | 32 | ~7 | | `double` | 64 | ~16 | Floating point numbers let a computer work with numbers of arbitrary precision. However, the limited digits of precision mean that a small number added to a large number can result in the number not changing. This results in odd scenarios such as: $$ x+(y+z)\neq(x+y)+z $$ ## References The ampersand (&) represents a reference variable and an argument passed into a parameter with an ampersand must be a valid lvalue. Effectively, it is a pointer, letting you do weird shit such as: ```cpp void inc(int &n) { n++; } ``` where the variable passed into `inc` will actually increase in the caller function. This can also be used in variable declarations to not create a second local variable: ```cpp #include double const &pi{M_PI}; // pi links back to M_PI ``` ## Arrays ```cpp // typename identifier[n]{}; int array[5]{}; int partial[3]{2}; int filled[3]{1, 2, 3}; ``` Arrays are contiguous in memory and default to 0 when initialised. If field initialised with values, the array will fill the first values as those values and set the rest to 0. Because arrays do not check bounds, `array[n+10]` or `array[-5]` will go to the memory address directed without complaint and ruin your day. ### Local arrays Local arrays cannot be assigned to nor returned from a function. If an array is marked `const`, its entries cannot be modified. Arrays can be passed to functions by reference (via pointer to the first entry). ## Memory !!! definition - **Volatile** memory is erased after the memory is powered off. - **Byte-addressable** memory is memory that has an address for each byte, such that to change a single bit the whole byte must be rewritten. Main memory (random access memory, RAM) is volatile and any location in the memory has the same access speed. An **address bus** with $n$ lines allows the CPU to update $n/8$ bytes at once (one address bit per line). The number of total memory addresses is limited by the number of lanes in the address bus. When a program is run, the operating system (OS) allocates a block of memory for it such that the largest address is at the bottom of the memory block for the program. - Instructions (the **code segment**) are stored at the **top** of the block - Constants (the **data segment**, including string literals) are stored **after** the instructions - Local variables (the **call stack**) are stored beginning from the **bottom** of the block Dynamically allocated variables and static variables are stored between the call stack and the data segment. ### Call stack The call stack represents memory and variables are allocated space from bottom to top. At the moment a function is run, its parameters are allocated space at the bottom, followed by all local variables that **may or may not** be defined. The return value of the function overwrites whatever is at the bottom of the function-allocated block such that the caller can simply reach up to get return data. !!! warning Arrays are allocated **top-down** such that indexing is made easy. ## C-style strings C-style strings are char arrays that end with a **null terminator** (`\0`). By default, char arrays are initialised with this character. If there is not a null terminator, attempting to access a string continues to go down the call stack until a zero byte is found.