# Week 1- Memory
##Team
Team name: Totally Useless Inc
Date: 09/02/2022
Members | Roles:
Ines Barnous-509518 | Facilitator
Adelina Muchanga-513966| Spokesperson
Tautvydas Barauskis-511325 | Reflector
Justas Antanaitis-516558 | Recorder
### Activity 1: Memory usage - the sizeof operator
 
- Yes there is a difference in the sizes reported.
In the compiler explorer: 
- No the size of a pointer doesn't depend on its type because their values are unsigned integer numbers which are adresses of other variables. Thus, pointers to all types of data occupy the same size of memory.
- So, the size of a pointer to a pointer should have the usual values, that is, 2 bytes for a 16-bit machine, 4 bytes for a 32-bit machine, and 8 bytes for a 64-bit machine.
### Activity 2: Array and structure sizes
```C
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);
}
```
The value of "Size of int array" is 40 because the size in bytes of an int is 4 and as we have 10 values in the array that will make the size become 40. On the other hand, the function "size_array" prints the size of the array parameter and that result is 8 bytes which makes sense because the argument is a pointer to the first element of an array and a pointer has the assigned size of 8 bytes on a 64 bit system.
The value of "Size of person_t structure" is 84 because in the bob instance created we have the char datatype which has a size of 1 and we have the array of this type with size 80 which makes the size become 80 and if we add the int age which has 4 bytes the size will be 84 in total(it will not always be the same situation because of padding).
The function "size_struct" prints the result 84 as expected because of the same reasons listed above.
### Activity 3: Memory addresses
```C
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);
}
```
int a memory allocation

short int b memory allocation

const char * s memory allocation

char buf[ ] memory allocation

short int c memory allocation

```C
printf("First character of const char *s: %c\n", &s[0]);
```
The string s ("Hello!") is stored considerably farther away from the varialbes a, b, and c. The variables are not layered in a contigious way in memory as there are some gaps (although not large ones) between them. That is not to say that the C language does not try to put everything as close as possible to save as much data as possible.
### Activity 4: Observing automatic lifetime
In each function every local variable had its own address, since two variables can not co-exist in the same address. However, local variables of diffrent function shared the same adrresses with another function's local variable addresses. In conclusion, if a variable is only used locally in a function, the variable of that address will be overwritten.
### Activity 5: Observing the stack
Like mentioned previously two variables can not co-exist in the same address. Therefore, even though two local variables are in different functions, if at least one of them is using other function's variable, it will not have the same address, the previously declared value will be overwritten by the new one.
### Activity 6: Leaking local addresses
In this activity, we have a local variable and a pointer to that variable's address that were declared in a function which was called from main. As this variable has a local-scope it will have automatic lifetime which ends after leaving that scope and entering a new one. In this case, the main called a void function thus entering a new scope. As a result, the local variable with the pointer, since it has become a dangling pointer, were overwritten by an array. This situation can be avoided if the variable was declared as static.
### Activity 7: Memory addresses of local variables
The compiler gives the error of "function returns address of local variable", referring to "return &array[0]"
that is in the create_int_array function. When the program is debugged it gives a "Segmentation fault" error as well.
* What do these warnings and / or errors mean and is it a good way to make an array during runtime?
The first error occurs because the function is returning the address of the first element of the array and an array doesn't consist of one single address but rather of many different ones.This way of creating an array during run time is not effective at all because an array isn't actually being formed because the create_intarray function is poorly written. The second error is giving a Segmentation fault because the main function is trying to access an array element that is out of bounds.
### Activity 8: Using malloc
```c
#include <stdio.h>
#include <stdlib.h>
int* allocate_memory(int count){
int* point=malloc(sizeof(int)*count);
free(point);
return point;
}
int main() {
int* long_int;
long_int=malloc(sizeof(unsigned long));
free(long_int);
float* numbers;
numbers= malloc(sizeof(float)*256);
free(numbers);
int count;
int* ints;
ints= allocate_memory(count);
free(ints);
return 0;
}
```
### Activity 9: Using allocated memory as an array
* The program allocates a memory block the size of 20 bytes. As it is known that an int data type take up 4 bytes, it will be possible to store 5 int elements at most in the allocated memory block
* Accessing an out-of-bounds array value will result in a garbage value printed out.
* There are some problems with this program:
* Firstly, the allocated amount of bytes via malloc is incorrect. Multiplying desired array size by the desired data type size will result in a correct allocation of memory:
```c
ptr = (int*) malloc(capacity*sizeof(int));
```
*
* Secondly, while malloc allocates the memory of desired size, it does not unfortunetally initialize the the returned bytes. This can be fixed by using calloc instead of malloc, which zeros the returned bytes.
```c
ptr = (int*) calloc(capacity, sizeof(int));
```
*
* Lastly, as the dinamically allocated memory is not freed, there is the potential risk of a memory leak occuring.
```c
#include <stdio.h>
#include <stdlib.h>
int main( void ) {
const int capacity = 20;
int *ptr = (int*) calloc(capacity, sizeof(int));
for (int i = 0; i < capacity; i++) {
printf("ptr[%d] = %d\n", i, ptr[i]);
}
free(ptr);
}
```
### Activity 10: Infinite memory?
This program doesn't crash on windows computers. It reaches 96% but doesn't go any further nor does it crash.
The program doesn't print anything since it doesn't crash so the program doesn't quit the 'while' loop.
* The whole computer crashes for me (Tautvydas) when there is no iteration checking and printing. This is possible when I change the value to 2^32 bytes (~4.3 gigabytes). If printing of iteration is added, it will not crash on windows computers.
### Activity 11: Fixing a memory leak
```c
#include <stdio.h>
#include <stdlib.h>
int main( void ) {
const unsigned int block_size = 1 << 100;
void *ptr = malloc(block_size);
int i = 0;
while (ptr != NULL) {
ptr = malloc(block_size);
printf("%d\n", i);
i++;
}
//Print null
printf("%d \n", ptr);
free(ptr);
}
```
### Activity 12: Dangerous **free**'s
-When passed a NULL pointer, the free() function doesn't have any effet on this pointer so there is no problem in using free() on a NULL pointer without using malloc.
-Free() uses the information given by the malloc function along with the address you give it in order to know exactly how much memory to free ( by declaring this memory as 'not in use'), so if you give it an address that has not been allocated, its behavior may be undefined.
### Activity 13: Using realloc
The amount of explicit calls to free that must be added in order to release all the memory allocated is one call to free new_grades because it is the one we just reallocated therefore the memory needs to be freed.
### Activity 14: Using a dynamically sized buffer
```c
/*
* The program listed below reads a file into memory, which it allocates dynamically. Initially, it
* allocates only a small amount of memory to hold the file’s contents. Therefore, the program will
* access the memory out of bounds, which crashes the program, after it has read some characters.
* Fix the program by reallocating a bigger chunk of memory once the count of characters stored
* reaches the current capacity. Use a factor of around 1.5 to increase the capacity each time it
* has been reached. Test the program by creating a file "input.txt", and pasting some contents
* into it.
*/
#include <stdio.h>
#include <stdlib.h>
int main( void ) {
char *ptr = NULL;
unsigned long capacity = 10;
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
*/
ptr[count++] = (char) c;
c = fgetc(file);
if (count == capacity) {
capacity *= 1.5;
ptr = realloc(ptr, sizeof(char[capacity]));
}
if(ptr == NULL) {
return 1;
}
}
free(ptr);
}
```
## Looking back
###### Reflection made by Tautvydas Barauskis
### What we've learnt
* We gained a general idea of how memory allocation works
* Data type sizes, their storing and lifetime
* Only some of us have a good understanding of the dynamically allocated memory
* The use of stack in memory allocation
### What were the surprises
* The amount of bugs that a casual programmer may encounter in every-day tasks.
* Furthermore, the amount of effort required to firstly find the bug and only then to come up with a solution.
* How in-depth it is possible to go in C language with binary
### What problems we've encountered
* I believe that the communication between group members was a bit hectic due to the way that the assignments in themselves are made. As there are multiple assignment that are required to be completed, it naturally feels that there should be a equal split of activities between the group memebers. Although, that way of thinking did not actually work out, as every activity is related to the previous one and it requires the knowledge obtained from those activities to complete the current one.
### What was or still is unclear
* Our team considers the dinamically allocated memory, memory leaks still conceptually unclear and would like additional explanation on that area.
### How did the group perform?
* There were no people that were exceptional during this week as everyone contributed a similar amount to the work they were assigned to.
* As nobody had any advanced knowledge concerning the activities we were doing, we had to primary rely on external sources of information to complete our assignments, which was a small set-back for our team.
* After understanding the way of working in this module, our team is certain that communication will be greatly improved.
## References
https://stackoverflow.com/questions/3463207/how-big-can-a-malloc-be-in-c
https://en.cppreference.com/w/c/memory/realloc
https://stackoverflow.com/questions/2259890/using-sizeof-on-mallocd-memory
https://www.techonthenet.com/c_language/standard_library_functions/stdlib_h/realloc.php
https://www.tutorialspoint.com/explain-lifetime-of-a-variable-in-c-language
https://www.tutorialspoint.com/c_standard_library/c_function_realloc.htm