# Week 1 - Memory
## Team
Team name: Stroopwafel
Date: 11-02-2022
Members
Coen Bunk
Dylan van Golberdinge
Martijn Gerritsen
| Role | Name |
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------|
| **Facilitator** keeps track of time, assigns tasks and makes sure all the group members are heard and that decisions are agreed upon. | Martijn |
| **Spokesperson** communicates group’s questions and problems to the teacher and talks to other teams; presents the group’s findings. |Coen|
| **Reflector** observes and assesses the interactions and performance among team members. Provides positive feedback and intervenes with suggestions to improve groups’ processes. |Coen|
| **Recorder** guides consensus building in the group by recording answers to questions. Collects important information and data. |Dylan|
## Activities
Make sure to have the activities signed off regularly to ensure progress is tracked.
Set up a project in CLion to write the small programs needed in some of the activities.
### Activity 1: Memory usage - the sizeof operator
```c=11
void calc_object_bytes() {
printf("char : %llu\n", sizeof(char));
printf("short int : %llu\n", sizeof(short int));
printf("int : %llu\n", sizeof(int));
printf("long int : %llu\n", sizeof(long int));
printf("long long int : %llu\n", sizeof(long long int));
printf("float : %llu\n", sizeof(float));
printf("double : %llu\n", sizeof(double));
printf("long double : %llu\n", sizeof(long double));
}
void calc_pointer_bytes() {
printf("char* : %llu\n", sizeof(char*));
printf("int* : %llu\n", sizeof(int*));
printf("float* : %llu\n", sizeof(float*));
printf("double* : %llu\n", sizeof(double*));
printf("void* : %llu\n", sizeof(void*));
}
int main() {
calc_object_bytes();
calc_pointer_bytes();
}
```
| Type | Bytes |
| ----------- | ----- |
| char | 1 |
| short int | 2 |
| int | 4 |
| long int | 8 |
|long long int| 8 |
|float | 4 |
|double | 8 |
|long double | 16 |
|char* | 8 |
|float* | 8 |
|double* | 8 |
|void* | 8 |
Does the size of a pointer depend on its type? Why (not)?
Nee, want een pointer verwijst naar een address in het geheugen, wat altijd 8 bytes is. ongeacht het type pointer.
### Activity 2: Array and structure sizes
This is how you include code listings in your markdown document:
```c=11
typedef struct {
char name[80];
int age;
} person_t;
void size_array(int array[]) {
printf("Size of array parameter: %lu\n", sizeof(array));
}
void size_struct(person_t p) {
printf("Size of p parameter: %lu\n", sizeof(p));
}
int main() {
int array[10];
person_t bob = {.name = "Bob", .age = 22};
printf("Size of int array: %lu\n", sizeof(array));
printf("Size of person_t structure: %lu\n", sizeof(bob));
size_array(array);
size_struct(bob);
}
```
Size of int array: 40
Size of person_t structure: 84
Size of array parameter: 8
Size of p parameter: 84
Geen verschil tussen person_t struc and p parameter omdat een struct in zijn geheel word gekopieërd, ook als deze word meegegeven aan een functie.
Er zit verschil tussen de int array en array parameter omdat de array veranderd word in een pointer wanneer je het aan een functie doorgeeft. De waarde van 8 bytes is het address waar de pointer naartoe verwijst, dit was ook te zien in de vorige activity(1)
### Activity 3: Memory addresses
```c=11
int main( void ) {
int a = 0xA0B0C0D0;
short int b = 0x7856;
const char * s = "Hello!";
char buf[] = "Pointer";
short int c = 0x3412;
printf("int a: %p\n", (void*) &a);
printf("short int b: %p\n", (void*) &b);
printf("const char *s: %p\n", (void*) &s);
printf("char buf[]: %p\n", (void*) buf);
printf("short int c: %p\n", (void*) &c);
het adr
printf("H : %p\n", (void*) &s[0]);
printf("e : %p\n", (void*) &s[1]);
printf("l : %p\n", (void*) &s[2]);
printf("l : %p\n", (void*) &s[3]);
printf("o : %p\n", (void*) &s[4]);
printf("! : %p\n", (void*) &s[5]);
}
```
| address | type |
| ----------- | ----- |
| 0x7ffffffee40c | int a |
| 0x7ffffffee408 | short int b |
| 0x7ffffffee410 | char *s |
| 0x7ffffffee420 | char buff[] |
| 0x7ffffffee40a | 8short int c |
| 0x8002004 | 'H' |
| 0x8002005 | 'e' |
| 0x8002006 | 'l' |
| 0x8002007 | 'l' |
| 0x8002008 | 'o' |
| 0x8002009 | '!' |
| 0x800200a | ' ' |
De characters van de string "hello" zijn niet valkbij opgeslagen van de variables a,b,s en c.
Het resultaat van het bekijken van de mem adres:
```
int a : 0000005AA934F884
short int b : 0000005AA934F8A4
const char *s : 0000005AA934F8C8
char buf[] : 0000005AA934F8E8
short int c : 0000005AA934F904
H : 00007FF77B08E14C
e : 00007FF77B08E14D
l : 00007FF77B08E14E
l : 00007FF77B08E14F
o : 00007FF77B08E150
! : 00007FF77B08E151
```
Het adres is van de eerste char van de string en dit is ver weg van de rest. De rest van de values zijn bij elkaar
### Activity 4: Observing automatic lifetime
```c=11
int add(int a, int b) {
int c = a + b;
printf("int a : %p\n", (void*) &a);
printf("int b : %p\n", (void*) &b);
printf("int c : %p\n", (void*) &c);
return c;
}
int mul(int x, int y) {
int z = x * y;
printf("int x : %p\n", (void*) &x);
printf("int y : %p\n", (void*) &y);
printf("int z : %p\n", (void*) &z);
return z;
}
int main() {
printf("Answer = %d\n", mul(add(3, 4), add(1, 5)));
}
```
int a : 0000009171CFFD90
int b : 0000009171CFFD98
int c : 0000009171CFFD64
int a : 0000009171CFFD90
int b : 0000009171CFFD98
int c : 0000009171CFFD64
int x : 0000009171CFFD90
int y : 0000009171CFFD98
int z : 0000009171CFFD64
Answer = 42
De memory adresses van a,b en c worden hergebruikt door x,y en z.
De compiler kan het adres hergebruiken omdat de liftimes van deze functies niet overeen komen.
Het resultaat wat in de return word teruggegeven staat op een ander address.
Dit kan je zien door het resulaat van de 2 adds in de mul regel apart te maken en hiervan het adres uit te printen.
### Activity 5: Observing the stack
```c=11
int poly(int a) {
int b = a * (a + 1);
printf("int a : %p\n", (void*) &a);
printf("int b : %p\n", (void*) &b);
return b / 2;
}
int add_polys(int x, int y) {
int bx = poly(x);
int by = poly(y);
printf("int x : %p\n", (void*) &x);
printf("int y : %p\n", (void*) &y);
printf("int bx : %p\n", (void*) &bx);
printf("int by : %p\n", (void*) &by);
return bx + by;
}
int main() {
printf("answer = %d\n", add_polys(42, 24));
}
```
Main roept add_polys aan met 2 getallen
in add_polys word de functie poly(x) en poly(y) aangeroepen. De addressen van a en b kunnen hergebruikt worden, dit omdat de addressen leeg gemaakt worden zodra de lifetime van die functie voorbij is. Voor de waardes van x, y, bx en by kunnen de addressen niet hergebruikt worden omdat deze al in gebruik zijn in de add_polys() functie, alleen nadat de add_polys functie klaar is kan het weer omdat het van de stack af is.
### Activity 6: Leaking local addresses
```c=11
int* get_answer( void ){
int answer = 42;
int* ptr_answer = &answer;
return ptr_answer;
}
void i_do_nothing( void ){
int no_answer[] = {24, 24, 24, 24};
}
int main() {
int* ptr_answer = get_answer();
printf("The answer is: %d\n", *ptr_answer);
i_do_nothing();
printf("The answer is: %d\n", *ptr_answer);
}
```
Het antwoord word overgeschreven door iets anders omdat answer eigenlijk van de stack af zou moeten zijn.
maar doordat er een pointer is die verwijst naar het adres waar de waarde 42 stond, is het er nog. Bij de i_do_nothing() functie word een array aangemaak met 4x de waarde 24 van het type int.
omdat "answer" van de stack gehaald is kan het adres dat daarvoor gebruikt was weer opnieuwe gebruikt worden.
Bij deze opdracht kregen wij verschillende resultaten:
1)
Het antwoord veranderde van 42 naar 24, omdat dus "answer" van de stack is en de ruimte hiervoor vrij is. int no_answer maakt gebruik van de vrije ruimte en verwijst naar een van de elementen in de int array.
2)
Er word niks met dat address gedaan, waardoor de console bij de tweede printf() statement een groot getal print.
```
The answer is: 42
1)
The answer is: 24
2)
The answer is: -858993460
```
### Activity 7: Memory addresses of local variables
```c=11
void do_some_work(int *values, int count) {
}
int * create_int_array() {
int array[10];
return &array[0];
}
int main() {
int * array = create_int_array();
for (int i = 0; i < 10; i++) {
array[i] = i + 1;
}
do_some_work(array, 10);
}
```
Warning: ```Function returns address of local variable [-Wreturn-local-addr]```
Het returnen van een locale variable wat niet mag omdat de lifetime ervan over is en dus van de stack afgegooid is.
Het werkt niet omdat de memory adres van de stack gegooid is vanwege het einde van de lifetime van deze functie. Daardoor kan het hergebruikt worden door de eerst volgende die daar belang bij heeft terwijl de pointer nog steeds naar. Doordat de pointer nog naar het adres wijst kan dit voor problemen kan zorgen als iemand via de pointer iets wil aanpassen.
### Activity 8: Using malloc
```c=11
int* allocate_memory(int count) {
return (int*) malloc(count * sizeof(int));
}
int main() {
unsigned long* ptr1 = (unsigned long*) malloc(1 * sizeof (unsigned long));
float* ptr2 = (float*) malloc(sizeof(float[256]));
int* ptr3 = allocate_memory(5);
}
```
### Activity 9: Using allocated memory as an array
```c=11
void activity_9() {
const int capacity = 20;
int *ptr = (int*) malloc(capacity * sizeof(int));
for (int i = 0; i < capacity; i++) {
printf("ptr[%d] = %d\n", i, ptr[i]);
}
}
int main() {
activity_9();
}
```
_How many int elements can be stored in the allocated block of memory:_
int capacity = 20
sizeof(int) = 4
20/4 = 5 elements.
_What happens when you perform an out-of-bounds access to an array that is stored in dynamically allocated memory:_
An error could occur, telling that there is no more memory/space reserved.
What's also possible is undefined behavior, it could cash, freeze or do other weird things.
_What is the problem in the program listed below, and how can it be fixed:_
After the malloc(capacity) the (* sizeof(int)) was not present, to fix this, replace the int *ptr = (int*) malloc(capacity); to int *ptr = (int*) malloc(capacity * sizeof(int));
### Activity 10: Infinite memory?
```c=11
void activity_10() {
int i = 0;
const unsigned int block_size = 1 << 28;
void *ptr = malloc(block_size);
while (ptr != NULL && i < 172) {
ptr = malloc(block_size);
i += 1;
printf("block_size = %d\n", i);
}
}
int main() {
activity_10();
}
```
Laptop || PC Coen: My laptop has 8gb ram and after reaching block size ~320.000 I stopped the program because I was running it already for 30+ minutes. In the end it went slower and slower because the memory windows used was already decreased as much as it could so that this program could still increase and it was taking longer to search for free spots in the memory.
PC Dylan: 87 Block sizes.
Laptop || PC Martijn: ~460.000
### Activity 11: Fixing a memory leak
```c=11
void activity_11() {
int i = 0;
const unsigned int block_size = 1 << 28;
void *ptr = malloc(block_size);
while (ptr != NULL && i < 172) {
ptr = malloc(block_size);
i += 1;
printf("block_size = %d\n", i);
free(ptr);
}
}
int main() {
activity_11();
}
```
### Activity 12: Dangerous `free`s
```c=11
void activity_12() {
int stack_variable = 42;
int* ptr = &stack_variable;
free(ptr);
int* null_ptr = NULL;
free(null_ptr);
}
int main() {
activity_12();
}
```
He programma crashed, dit komt omdat je geen geheugen kan vrijmaken wanneer er geen geheugen is toegewezen.
### Activity 13: Using realloc
```c=11
void activity_13() {
float *grades = NULL;
for (int capacity = 10; capacity <= 100; capacity += 10) {
float *new_grades = (float*)realloc(grades, sizeof(float[capacity]));
if (new_grades != NULL) {
grades = new_grades;
}
else {
free(new_grades);
}
}
free(grades);
}
int main() {
activity_13();
}
```
### Activity 14: Using a dynamically sized buffer
```c=11
#include <stdio.h>
#include <stdlib.h>
int main( void ) {
char *ptr = NULL;
unsigned long capacity = 20;
ptr = realloc(ptr, sizeof(char[capacity]));
if (!ptr){
fprintf(stderr, "Memory allocation failed\n");
return 1;
}
FILE *file = fopen("input.txt", "r");
if (!file) {
fprintf(stderr, "Error opening file\n");
return 1;
}
unsigned long count = 0;
int c = fgetc(file);
while (c != EOF) {
/* re-allocate memory pointed to by ptr if count == capacity
* don't forget to check if the pointer returned by realloc is not NULL
*/
if(count == capacity){
capacity = (unsigned long ) capacity *1.5;
ptr = realloc(ptr, sizeof(char[capacity]));
if (!ptr){
fprintf(stderr, "Memory allocation failed\n");
return 1;
}
}
printf("%lu\n", capacity);
ptr[count++] = (char) c;
c = fgetc(file);
}
return 1;
}
```
## Looking back
### What we've learnt
Wat beter mijn taak als recorder uitvoeren.
geleerd dat variabelen kunnen veranderen in pointers wanneer je deze meegeeft aan een functie. activity(2)
Verder ook een goede introductie gehad over memory (stack, heap en dynamic memory)
### What were the surprises
Dat er bij sommige groepsleden andere resultaten kwamen.
### What problems we've encountered
Momenteel nog geen problemen.
### What was or still is unclear
Nothing really
### How did the group perform?
De groep werkte goed samen, wanneer er iemand een problemen had met de code of het begrijpen van de stof hielpen wij elkaar.
> Written with [StackEdit](https://stackedit.io/).