# Week 1 - Memory ## Team Team name: Error Date: 8th February 2022 Members | Role | Name | |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------| | **Facilitator** keeps track of time, assigns tasks and makes sure all the group members are heard and that decisions are agreed upon. |Minh 511907 | | **Spokesperson** communicates group’s questions and problems to the teacher and talks to other teams; presents the group’s findings. |Kaloyan 511216 | | **Reflector** observes and assesses the interactions and performance among team members. Provides positive feedback and intervenes with suggestions to improve groups’ processes. |Thanh 516467 | | **Recorder** guides consensus building in the group by recording answers to questions. Collects important information and data. |Hoa 487272 | ## 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 | |--- |--- | | character | 1 | | short int | 2 | | int | 4 | | long int | 8 | | long long int | 8 | | float | 4 | | double | 8 | | long double | 16 | |Point data type|Size in bytes | |--- |--- | | char* | 8 | | int* | 8 | | float* | 8 | | double* | 8 | | void* | 8 | **Answer** • I do not see any differences in the size reported from online compiler explorer • The size of a pointer do not depend on data type but depend upon the operating system instead. For example, the size of data can be 4 bytes with a 32 bit computer and 8 bytes with a 64 bit computer. ### Activity 2: Array and structure sizes This is how you include code listings in your markdown document: ```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); } ``` **Answer:** ```C Size of int array: 40 Size of person_t structure: 84 Size of array parameter: 8 Size of p parameter: 84 ``` There is difference between the size printing as the size of a local variable and the size printing as of an argument passed to a function. It can be explained that when an array is sent to a function as a parameter, the array will be treated as a pointer, hence the sizeof array parameter function will return the size of the first element of the array (pointer’size) instead of the size of the array. ### 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); } ``` **Answer:** ```C int a: 0x7ffeeb342aa4 short int b: 0x7ffeeb342aa0 const char *s: 0x7ffeeb342aa8 char buf[]: 0x7ffeeb342ab0 short int c: 0x7ffeeb342aa2 first character of string: 0x558af0124008 ``` * The characters of the string "Hello" were not stored near the variables a, b, s, and c * The variables layed out in a contiguous way in memory ### Activity 4: Observing automatic lifetime ```C int add(int a,int b) { printf("Memory address of a in add function: %p\n",(void*)&a); printf("Memory address of b in add function: %p\n",(void*)&b); int c=a+b; printf("Memory address of c in add function: %p\n",(void*)&c); return c; } int mul(int x,int y) { printf("Memory address of x in mul function: %p\n",(void*)&x); printf("Memory address of y in mul function: %p\n",(void*)&y); int z=x*y; printf("Memory address of z in mul function: %p\n",(void*)&z); return z; } int main(void) { printf("%d\n",mul(add(3,4),add(1,5))); } ``` **Here is the result of the code:** Memory address of b in add function: 0061FEC4 Memory address of c in add function: 0061FEAC Memory address of a in add function: 0061FEC0 Memory address of b in add function: 0061FEC4 Memory address of c in add function: 0061FEAC Memory address of x in mul function: 0061FEC0 Memory address of y in mul function: 0061FEC4 Memory address of z in mul function: 0061FEAC 42 * As we can see, the variable x, y and z of mul function reuse the addresses of variable a, b and c of add function. This occurs as whenever a function is called in C, C will use Stack memory. After the function returns, it will be deallocated in the memory. In other words, it will be destroyed . ### Activity 5: Observing the stack ```C int poly(int a){ printf("Memory address of a in poly function: %p\n",(void*)&a); int b=a*(a+1); printf("Memory address of b in poly function: %p\n",(void*)&b); return b/2; } int add_polys(int x,int y) { printf("Memory address of x in add_polys function: %p\n",(void*)&x); printf("Memory address of y in add_polys function: %p\n",(void*)&y); int bx=poly(x); int by=poly(y); printf("Memory address of bx in add_polys function: %p\n",(void*)&bx); printf("Memory address of by in add_polys function: %p\n",(void*)&by); return bx+by; } int main(void){ printf("%d\n",add_polys(42,24)); } ``` **And here is the result of code:** Memory address of x in add_polys function: 0061FEC0 Memory address of y in add_polys function: 0061FEC4 Memory address of a in poly function: 0061FE90 Memory address of b in poly function: 0061FE7C Memory address of a in poly function: 0061FE90 Memory address of b in poly function: 0061FE7C Memory address of bx in add_polys function: 0061FEAC Memory address of by in add_polys function: 0061FEA8 1203 * From the result, we can see that the addresses of both local variable a and b are reused after the function poly is called. That is because after the function returns, the memory addresses of local variables inside function will be deallocated. In other words, they will be destroyed. ### 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 ); } ``` **Here is the result:** The answer is: 42 The answer is: 24 * We can see that from the result the original value 42 which pointer Ptr_answer obtains from get_answer() function is replaced by value 24 of i_do_nothing() function. This changing happens because after the return of get_answer() function, pointer ptr_answer get the address of local variable answer. At this time, the address still holds value 42; however, after the calling of i_do_nothing() function, the address which is holding value 42 is reused, and value 42 is replace by value 24. ### Activity 7: Memory addresses of local variables ```C void do_some_work(int *value,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[1]=1+1; } do_some_work(array,10); } ``` * The warning from the compiler: address of local variable 'array' returned. * This means that the compiler tries to warn user that they are using local variable in one function, which will be destroyed after the function returns. This is not a good way to create an array at runtime work because the content of the array will be destroyed, and we loose those data stored in local array. ### Activity 8: Using malloc ```C int* allocate_memory(int count){ int *ptr = (int*) malloc(count*sizeof(int));//allocate memory to store int return ptr;//return the pointer } int main() { unsigned long *long_number; float *float_number; int count = 10; long_number = (unsigned long*) malloc(1*sizeof (unsigned long ));//allocate memory to store one unsigned long number float_number = (float*) malloc(256 * sizeof (float));//allocate memory to store 256 float numbers //Memory addresses printf("Memory address stored in long_number %p\n",(void*)long_number); printf("Memory address stored in float_number %p\n",(void*)float_number); printf("Memory address for int %p",allocate_memory(count)); //de-allocate the memory. free(long_number); free(float_number); return 0; } ``` ### Activity 9: Using allocated memory as an array ```C //fixed code int main( void ) { const int capacity = 20; int *ptr; ptr = (int*) malloc(capacity * sizeof (int));//allocate memory to store 20 int for (int i = 0; i < capacity; i++) { ptr[i] = i;//add value printf("ptr[%d] = %d\n", i, ptr[i]); } } ``` **Answer question** * How many int elements can be stored in the allocated block of memory? : 20 * What happens when you perform an out-of-bounds access to an array that is stored in dynamically allocated memory?: 1.Segmentation fault or 2.Return a wrong value (0) * What is the problem in the program listed below, and how can it be fixed? int *ptr = (int*) malloc(capacity) -> ptr = (int*) malloc(capacity * sizeof (int)) ### Activity 10: Infinite memory? ```C int main( void ) { const unsigned int block_size = 1 << 28; void *ptr = malloc(block_size); while (ptr != NULL) { ptr = malloc(block_size); printf("%p\n",ptr); } } ``` **Answer question** Why does the program crash, and how many blocks can be allocated before the program crashes? * Add printf calls to check this. Infinite blocks can be allocated before the program crashes This program does not deallocate the ptr ,so it crashes due to the while loop Which is continuously allocate more memory with the size of $2^{28}$ bytes per block. ### Activity 11: Fixing a memory leak ```C int main( void ) { const unsigned int block_size = 1 << 28; void *ptr = malloc(block_size); if (ptr != NULL) { ptr = malloc(block_size); printf("%p",ptr); } free(ptr); } ``` ### Activity 12: Dangerous `free`s ```C int main() { int stack_variable = 42; int *ptr = &stack_variable; printf("%p\n",ptr); free(ptr); printf("%p\n",ptr); int *null_ptr = NULL; printf("%p\n",null_ptr); free(null_ptr); printf("%p\n",null_ptr); return 0; } ``` ![](https://i.imgur.com/O0nfnvj.png) **Answer:** A pointer, which has a value and not obtained by a malloc() and freed by free() will cause the program to crash as the memory that is pointing it is not actually allocated and at the same time is freed so there is nothing to be printed if printf() function for that pointer is called. A NULL pointer has the same case as the one that is not obtained. Here even there is no value or variable to which it points so there is no address. Freeing function does nothing in this case. ### Activity 13: Using realloc ```C int main(void) { 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; } free(new_grades); } free(grades); return 0; } ``` ### Activity 14: Using a dynamically sized buffer ```c int main(void) { char *ptr = NULL; unsigned long capacity = 20; ptr = (char *) malloc(capacity); printf("Address = %s\n", ptr); 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) { ptr[count++] = (char) c; c = fgetc(file); if (count == 1) { ptr = (char *) realloc(ptr, 1.5 * capacity); printf("Address = %s\n", ptr); free(ptr); } } return 0; } ``` ## Looking back ### What we've learnt **Hoa:** The first 3 activities help me to revise the knowledge of memory, the size of different data types and understand clearer how different data will be stored to the allocated memory. Those activities also give me more information about how “sizeof” operator works. **Minh:** I think that we've learned how to avoid mishandling memory management in C/C++. For my part, I think I learned about memory layout in C/C++, how a function is called, the stack data structure and how computers use it. **Thanh:** The “malloc” or “memory allocation” method in C is used to dynamically allocate a single large block of memory with the specified size. It returns a pointer of type void which can be cast into a pointer of any form. The free() function in C library allows you to release or deallocate the memory blocks which are previously allocated by calloc(), malloc() or realloc() functions. It frees up the memory blocks and returns the memory to heap. It helps freeing the memory in your program which will be available for later use. **Kaloyan:** Things I learned from the activities are the using of malloc, free and realloc functions and some new thing about the dynamic memory. Other useful thing is that I revised some of the material about the size of types and memory occupation. ### What were the surprises There are no surprising things. ### What problems we've encountered There are no problem until now ### What was or still is unclear There are no unclear ### How did the group perform? I think that the group performed very well and in accordance with each other. > Written with [StackEdit](https://stackedit.io/).