# Week 1 - Memory ## Team Team name:BeastFromTheEast Date of upload: Members: Nikola Zahariev, Casian Ionescu, Attila Poldan, Alexandra Melei | Role | Name | |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------| | **Facilitator** keeps track of time, assigns tasks and makes sure all the group members are heard and that decisions are agreed upon. | | | **Spokesperson** communicates group’s questions and problems to the teacher and talks to other teams; presents the group’s findings. | | | **Reflector** observes and assesses the interactions and performance among team members. Provides positive feedback and intervenes with suggestions to improve groups’ processes. | | | **Recorder** guides consensus building in the group by recording answers to questions. Collects important information and data. | | ## 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 | Data type | Size in bytes | | --------------- | -------------------------- | | char | 1 | | short int | 2 | | int | 4 | | long int | 4 | | long long int | 8 | | float | 4 | | double | 8 | | long double | 16 | | char* | 8 | | int* | 8 | | float* | 8 | | double* | 8 | | void* | 8 | ### 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));//10 int values, 4 bytes each => 40; printf("Size of person_t structure: %lu\n", sizeof(bob)); size_array(array);//the allocated space in memory for the array; size_struct(bob); } ``` The results of the prints are: Size of int array: 40 - 10 int values, 4 bytes each => 40; Size of person_t structure: 84 Size of array parameter: 8 - this is the allocated space in memory for the int array; Size of p parameter: 84 ### 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); } ``` Table containing the variables with their values and address: | variable | value | address | | ---------------| -------------------------- |-------------------| | int a | -1599029040 | 0x000000000061FE1C| | short int b | 30806 | 0x000000000061FE1A| | char *s | Hello! | 0x000000000061FE10| | char buf[] | Pointer | 0x000000000061FE08| | short int c | 13330 |0x000000000061FE06| There are gaps between the variables in memory. ### Activity 4: Observing automatic lifetime ```c= int add(int a, int b) { int c = a + b; printf("Memory address of variable a: %p\n",(void*)&a); printf("Memory address of variable b: %p\n",(void*)&b); printf("Memory address of variable c: %p\n",(void*)&c); return c; } int mul(int x, int y) { int z= x*y; printf("Memory address of variable x: %p\n",(void*)&x); printf("Memory address of variable y: %p\n",(void*)&y); printf("Memory address of variable z: %p\n",(void*)&z); return z; } int main(void) { printf("%d\n",mul(add(3,4),add(1,5))); } ``` Record your answer here: Only 3 address are used. They were repeating but, the variables that were used for the calculation stop being alive variables, after their purpose is done, the calculation. ### Activity 5: Observing the stack ```c= int poly(int a) { int b = a * (a + 1); printf("Memory address of variable a: %p\n",(void*)&a); printf("Memory address of variable b: %p\n",(void*)&b); return b / 2; } int add_polys(int x, int y) { int bx = poly(x); int by = poly(y); printf("Memory address of variable x: %p\n",(void*)&x); printf("Memory address of variable y: %p\n",(void*)&y); printf("Memory address of variable bx: %p\n",(void*)&bx); printf("Memory address of variable by: %p\n",(void*)&by); return bx + by; } int main( void ) { printf("Result: %d\n", add_polys(42, 24)); } ``` Record your answer here: The basics of this activity are the same as the previous one. The addresses here get used multiple times by local variables (the address of "a" and "b") ### Activity 6: Leaking local addresses ```c= 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( void ) { int* ptr_answer = get_answer(); printf("The answer is: %d\n", *ptr_answer); i_do_nothing(); printf("The answer is: %d\n", *ptr_answer); } ``` Record your answer here: The value of ptr_answer is shown correct the first time it's printed. After we run the i_do_nothing() function, we try to print it again and it shows one of the values stored in an array in that function. From checking the debugger, I noticed after it gets printed the value that was stored in that part of memory is 0. Then we call the function i_do_nothing() and when we print again it as the value 24. from what I can understand when it gets used the first time the value is 42. Then the value is no longer needed, so it is no longer live, meaning the pointer we got from the get_answer() function points to a 0; Because they are in the same scope, when the i_do_nothing() function gets called the values(or at least one) get allocated in the same address as where the value 42 was kept. ### Activity 7: Memory addresses of local variables ```c= void do_some_work(int *values, int count); int * create_int_array() { int array[10]; return &array[0]; } int main( void ) { int * array = create_int_array(); for (int i = 0; i < 10; i++) { array[i] = i + 1; } do_some_work(array, 10); } ``` Errors: 1: undefined reference to `do_some_work' - occur when some function definition is missing from the script. 2: collect2.exe: error: ld returned 1 exit status - rror is the consequence of previous errors. In this example there is an earlier error - undefined reference to 'clrscr' - and this is the real one. The exit status error just signals that the linking step in the build process encountered some errors. Normally exit status 0 means success, and exit status > 0 means errors. ### Activity 8: Using malloc ```c= int main() { unsigned long *longNum; longNum = (unsigned long*) malloc(1); float *floatNum; floatNum = (float*) malloc(256); } int* allocateMemory(int count) { int *allocator; allocator = (int*) malloc(count); return allocator; } ``` ### Activity 9: Using allocated memory as an array Record your answer here: We can store 20 integer elements because we have allocated that much memory. When writing/reading from out of bounds in a dinamically allocated array nothing happens. It reads and writes to the requested memory space even if it is out of bounds. There is no check for the case when we read outside of the allocated space. A check could be implemented. There is also no check for the occasion when the memory allocation fails. An if statement could be implemented right after the allocation to check if the retuned pointer is a NULL pointer. ### Activity 10: Infinite memory? Record your answer here: 90 blocks can be allocated for Attila 187 block of memory can be allocated for Nikola 196 blocks of memory can be allocated for Casian ```c= int main( void ) { const unsigned int block_size = 1 << 28; int count = 0; void *ptr = malloc(block_size); while (ptr != NULL) { ptr = malloc(block_size); count++; printf("%d --> %d\n",count,ptr); } } ``` ### Activity 11: Fixing a memory leak Record your answer here: ```c= auto count{0}; const unsigned int block_size = 1 << 28; void *ptr = malloc(block_size); while (ptr != NULL) { ptr = malloc(block_size); printf("%d --> %d\n",count,ptr); count++; free(ptr); } 1. * // also here works and might be better // free(ptr); ``` ### Activity 12: Dangerous `free`s Record your answer here: The question is disected into two different answers: a) For the nullpointer calling free(NULL) will have result into no action ocurring. The free() function makes it so that the space that the NULLptr is pointing to is deallocated and can be alloced further. So, calling free(NULL) will result into no actions. b) If we try to call free(ptr) where ptr is a raw pointer (so not a result of a previous call to malloc(), realloc(), calloc()) the output results into heap corruption. That means, the compiler is trying to free up space in the memory for the address's value the ptr is pointing to (this memory ptr is point to is on the heap). Hence, the function's output is expressed in the error code "(0xC0000374)" - used to describe memory leaks. ### Activity 13: Using realloc Record your answer here: Only one is needed. The realloc function deallocates memory on a succesfull reallocation. Only one free is needed at the end of the program to deallocate the memory. ### Activity 14: Using a dynamically sized buffer ```c= char *ptr = NULL; unsigned long capacity = 20; ptr =(char*) 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=capacity*1.5; ptr =(char*) realloc(ptr, sizeof(char[capacity])); } ptr[count++] = (char) c; c = fgetc(file); } printf("%s",ptr); ``` ## Looking back ### What we've learnt Formulate at least one lesson learned. ### What were the surprises Fill in... Attila: When we allocate memory with malloc and we try to read outside/write outside it does not produce an error code. ### What problems we've encountered Fill in... ### What was or still is unclear Fill in... Attila: When we allocate a bunch of memory and then we try to free up the previously allocated memory blocks how do we go back to free them up? ```c= while(ptr) { ptr=malloc(block_size); } ``` This reserves a bunch of memory, but every time it reassigns the pointer value of ptr losing the previous values. Is there a way to count back like ptr[-2] or should I make a variable that can remember the previous pointer values? ### How did the group perform? How was the collaboration? What were the reasons for hick-ups? What worked well? What can be improved next time?