Hello everyone! Welcome to another post. This time, it will be about ways to store data in memory. So, lets begin! First one is a static array. Static array is just an array of things in a row, each behind the last one and with no information about where its supposed to end. Lets show it on our old friend, the imaginary 4bit computer with 16 4 bit integers of ram(random access memory). Lets say that the array is begining at the slot 3, and has lenght of 6. The pointer to it and its lenght are stored externally, lets say in slot 0 and 1. |0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15| |3|6|0|1|2|3|4|5|6|0| 0| 0| 0| 0| 0| 0| Now, why pointers? Well, the arrays are commonly referenced by their locations in memory. Common programming languages even have a special operator type([]theses brackets), for selecting which field of the array do we want to access. So you can represent our array from the previous example as this: i4 array[6]; and we can then edit the values of them like this!: array[1] = 1; array[2] = 2;... We can also read data from the back obviously, the exact same way we wrote to them: i4 data = array[5]; What hapens if you go out of bounds of the array, aka. the field you are trying to access is bigger than the array? Well, the result can be preety random, depending on the computer and the kernel, but if you are running an operating system with normal security measures, it will crash your program with a segfault(mentioned in one of previous posts), but since this is a 4bit computer uncapable of running any os, we can assume it will just fetch the memory. Well, how do we access the memory manually, without any fancy array access operators? Simply, we can just edit the pointer, like this: *(array + 2) = 10; and access it the same way. You must also keep in mind however, that you must always multiply the number you are adding to the pointer by the amount of fields(bytes) that the array consists of. Simply, when you have normal 32bit integer, it takes up 4 bytes, so you must do *(array + 2 * 4) = 10; (32bits / 8bits(byte) = 4bytes per int32). Thats about whats there for the static arrays, but static array are boring.. lets move onto dynamic arrays! Dynamic arrays are just like static arrays, with one key difference, thus they can edit their own size. How do these array commonly work? Well, they have set capacity and size, and if the size acceeds the capacity the capacity commonly doubles(making the capacity commonly power of 2). In real world it will look something like this: We have dynamic array with its pointer on field 0, capacity on field 1, size on field 2, and the array begining on field 3 |0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15| |3|4|4|1|2|3|4|0|0|0| 0| 0| 0| 0| 0| 0| Now however, lets say the programmer pushed one more value to the dynamic list, making the size overflow the capacity, lets see what did the dynamic array do! |0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15| |3|8|5|1|2|3|4|5|0|0| 0| 0| 0| 0| 0| 0| The array has doubled the capacity. The OS also has dynamic allocator, which manages the memory allocation for the whole operating system. So, how would this work on normal computer with operating system? Well, the code looks something like this: int* ptr; // the size overflowed here: int* ptr2 = malloc(capacity * 2); // here, the content of ptr1 are copied to the new array ptr = ptr2; // overwrite the old pointer with the new allocation So, thats about all of it for dynamic arrays, lets look at linked lists now, shall we? Linked lists are preety complex topic, but I will try to keep it as simple as possible while keeping it correct. In static and dynamic arrays, the values were always right behind eachother, meaning one value + 1 was always the next value, etc. But linked lists are not right behind eachother. Each value consists of the value itself, and pointer to the next value. This allows you that you can just delete anything from the end and the start and even in the middle very easily, sometimes it can be preety bad however when you're going trough thousands of fields, because unlike any other type of array mentioned above, the performance was not affected in any way by the number of the field you're reaching for. So, when there will be field 2000, it will be twice as slow as if it was field 1000. Lets represent the linked list graphically: m = memory field location value = the value ptr = the pointer to next field [m][value][ptr] 1 24 40 25 0 1 40 8 25 So, lets try to order this list.. first we begin at location 1, with value 40, that points to field 40, which contains 8, and that points to location 25 with value of 0, but that points back to 1...? Well, this is a representation of an infinite linked list, where some value at the end points to the start and around and around. It doesn't have to point to the start to be infinite tho, the value can for example point to itself, and various other things. Now lets get to trees, which is preety complex topic, but lets do it anyway! Trees are represtnation of each branch having multiple branches that each has multiple branches... . Obviously these are bit too complex for our 4bit memory imaginary computer, so lets switch to graphical representation: 1-10 / \0 N / \1 \1 This is an example of binary tree, where each branch can only eighter contain value meaning its the end, or has two other branches coming from it. Common example of tree usage is json or yaml for example. You can use these for example for creating a complex calculator app, for calculating things such as this: (3+2) * 4 + 20 = We can simply put this into a tree, looking like this: +_ * \ / \ \ + 4 \ /\ \ 3 2 20 And after doing thing like this this, it should be preety simple to calculate each position in the tree, and you have the result! This is about everything for now, obviously there are data structures, but I am not smart nor (!tired) enough to cover them here. So, see you next time!