---
# System prepended metadata

title: Week 1 - Memory

---

# Week 1 - Memory

## Team

Date: 22/02/2023

Members:
- Sean Bos [532785]
- Boxing Jiang [521516]


### Activity 1: Printing memory addresses - I

Give the memory address ranges of the two arrays, `integers` and `doubles`, in the code listed below.
Explain why these ranges do or do not overlap.


```C
#include <stdio.h>

int sum_ints(void) {
    int integers[1024] = {1};
    printf("Address of 'integers': %p \n", (void *) &integers);
    printf("Size of integers is: %zu \n", sizeof(integers));

    for (int i = 1; i < 1024; ++i) integers[i] = integers[i - 1] + 1;
    return integers[1023];
}

double mul_doubles(int init) {

    double doubles[512] = {init};
    printf("Address of 'doubles': %p \n", (void *) &doubles);
    printf("Size of integers is: %zu \n", sizeof(doubles));
    for (int i = 1; i < 512; ++i) doubles[i] = doubles[i - 1] * 0.999;
    return doubles[511];
}

int main(void) {
    double result = mul_doubles(sum_ints());
    printf("Result = %lf\n", result);
}


```
From my machine, both arrays don't share the same address, but their range may or may not overlap. It depedns  However, if they have a common size, they would share the same address.

It works that way because the function `sum_ints` is executed first and ends before the function `mul_double` since the `main` function calls `mul_double` with the argument of whatever's returned in `sum_int`. `sum_ints` returns the last element of the local array variable  `integers`. The local array variable `double get the value of the last element of `integers` thus. 

This means it is impossible for both variables to be alive on the same time on the stack, so therefore their memory range can overlap if the size of `doubles` can fit inside the location formerly occupied by `integers`.

The images below shows the observation:


![](https://i.imgur.com/gBKHfQA.png)

If I shorten the length of `double` by half, both of them would have the same size.
![](https://i.imgur.com/BfekHfc.png)


`integers` takes 4096 bytes since it is an array that stores 1024 ints. 1 int is 4 bytes (on my program) so it's size is 4 * 1024 = 4096 bytes.
`doubles` has 8192 bytes since it's an array with 1024 doubles. 1 double takes 8 bytes (on my program) so the size is 8 * 1024 = 8192bytes.
In total, the program is expected to store 12288 bytes for the arrays.

### Activity 2: Printing memory addresses - II

Record the answer to the activity's questions here.

```C
#include <stdio.h>

int sum_ints(void) {
	int integers[1024] = {1};
	for (int i = 1; i < 1024; ++i) integers[i] = integers[i - 1] + 1;
	return integers[1023];
}

double mul_doubles(void) {
	double doubles[1024] = {sum_ints()};
	for (int i = 1; i < 1024; ++i) doubles[i] = doubles[i - 1] * 0.999;
	return doubles[1023];
}

int main(void) {
	double result = mul_doubles();
	printf("Result = %lf\n", result);
}
```
The memory addresses are once again different altough the program works the same. The memory addresses do not overlap no matter the size now. They do not because `sum_ints` is executed while `doubles` is defined. This means `doubles` and `integers`must be alive at the same time on the stack

The program once again is expected to store 12288 bytes for the arrays.

![](https://i.imgur.com/bKEfEPu.png)
![](https://i.imgur.com/wwI6qoF.png)



### Activity 3: Using data that is no longer alive

Record the answer to the activity's questions here.

```c
#include <stdio.h>

int * create_array(void) {
	int array[10];
	return array;
}

void print_array(int values[], size_t size) {
	printf("[%d", values[0]);
	for (size_t i = 1; i < size; ++i) printf(", %d", values[i]);
	printf("]\n");
}

int main(void) {
	int * values = create_array();
	for (int i = 0; i < 10; ++i) values[i] = i + 1;
	print_array(values, 10);
}
```

The compiler gives the following message: 

![](https://i.imgur.com/ruEzCut.png)


The error means that there would be undefined behavior if you pass the address of a local vairable to another function.

This approach doesn't work because you can't pass the address of a local variable. If the variable array `array` is static or global, then the entire program will work as intended. It will also work if you try to use dynamic memory as well.


### Activity 4: Using malloc

Record the answer to the activity's questions here.

```
#include <stdio.h>
#include <stdlib.h>

int* allocate_memory(int count){

    int *countMem;
    countMem = (int *) malloc(sizeof(int) * count);

    if (countMem == NULL){

        printf("MEemroy allocation failed. \n");
    }
    return countMem;

}

int main(void) {

    unsigned long int *number1;
    float *floatArray;

    number1 = (unsigned long int*) malloc(sizeof(unsigned long int));
    if (number1 == NULL){

        printf("MEemroy allocation failed. \n");
    }

    floatArray = (float*) malloc(sizeof(float) * 256);
    if (floatArray == NULL){

        printf("MEemroy allocation failed. \n");
    }

    free(number1);
    free(floatArray);

    int *p_countMem = allocate_memory(5);

    free(p_countMem);

}
```

### Activity 5: Allocating zero bytes

I wrote the following program:
```c
#include <stdio.h>
#include <stdlib.h>

int main(){

    int *ptr = (int *) malloc(0);

    printf("%p", ptr);

    free(ptr);
}
```
This is the result:
![](https://i.imgur.com/0GfHDNK.png)


Malloc return the address of the memory allocated.

• What happens if you try to store data in the block of memory obtained by malloc (by
storing a value at the address that was returned), and why does that happen?

There would be undefined behavior as the behavior is  implementation-defined. The C standard says some scenarios can be that NULL can be return or it would function like normal (as if you allocated enough size)

My program does this:
#include <stdio.h>
#include <stdlib.h>

```c
int main(){

    int *ptr = (int *) malloc(0);

    printf("Value: %d\n", *ptr);

    *ptr = 12;

    printf("Value: %d", *ptr);

    free(ptr);
}
```
The result is this:
![](https://i.imgur.com/IExUdUf.png)

It somehows functions like normal.



• Is it possible to allocate a block of memory that has a negative size?

It is not possible to do that. I tried to allocate a memory with size -1 and the console shows this:

![](https://i.imgur.com/K4tqoxv.png)



### Activity 6: Using allocated memory as an array

Record the answer to the activity's questions here.

```c
int * create_array(size_t capacity) {
    int *ptr = (int*) malloc(capacity);
    return ptr;
}

int main( void ) {
    
    const size_t capacity = 24;
    int * array = create_array(capacity);
    
    for (size_t i = 1; i <= capacity; i++) array[i] = 42;
    
    for (size_t i = 1; i <= capacity; i++) {
        printf("array[%zu] = %d\n", i, array[i]);
    }
}
```


1. How many int elements can be stored in the block of memory allocated by the create_array function?

    The memory size is 24 bytes so 6 ints can be stored.

2. What happens when you perform an out-of-bounds access to an array that is stored in dynamically allocated memory?

    You will access junk data.


3.  What are the problems in the program listed below, and how can they be fixed (Include the fixed program into your logbook)?

    The problem is that the allocated memeory isn't deallocated which would result in memory leak. It can be fixed by using the free() function to deallocate the memory.
    
    This is my solution:
    
```c

#include <stdio.h>
#include <stdlib.h>

int * create_array(size_t capacity) {
     int *ptr = (int*) malloc(capacity);
    return ptr;
}

int main( void ) {
    const size_t capacity = 24;
    int * array = create_array(capacity);
    for (size_t i = 1; i <= capacity / sizeof(int) - 1; i++) array[i] = 42;

    for (size_t i = 1; i <= capacity / sizeof(int) - 1; i++) {
        printf("array[%zu] = %d\n", i, array[i]);
    }

    free(array);
}
```
    

### Activity 7: Fixing a memory leak

This is my solution for the program:

```c
#include <stdio.h>
#include <stdlib.h>

int main(void) {
	const int size = 1024 * 1024;
	for (int i = 0; i < size; ++i) {
		int * ptr = (int*) malloc(sizeof(int[size]));
		if (ptr != NULL) ptr[0] = 0;
        free(ptr);
	}
	puts("All done!");
}
```

I used the free() function to make sure the allocated memory gets deallocated when it is no longer used.

In the for loop, everytime the loop iterates, a new memory is allocated since malloc is used. 

I added a statement that frees the allocated memory.





### Activity 8: Dangerous `free`s

I made a simple program that tries to free a pointer that's not created with dynamic memory:



```c
#include <stdlib.h>

int main(){

    int x = 10;

    int *ptr = &x;

    free(ptr);
}
```

The compiler shows the following:
![](https://i.imgur.com/HfEPTw9.png)

It gives a warning that you are freeing non-dynamic memory. It means undefined behavior may happen in my program.


I made another program that frees a null pointer:
```c
#include <stdlib.h>

int main(){

    int *ptr = NULL;

    free(ptr);
}
```
The compiler gives no message. The program appears to be valid. We observed that freeing a null pointer does nothing.


### Activity 9: Using realloc


```c
int main( void ) {
	float *grades = NULL;
	size_t capacity = 1024;
	for (int count = 0; count < 10000; capacity += 1024, ++count) {
		float *new_grades = (float*)realloc(grades, sizeof(float[capacity]));
		if (new_grades != NULL){
			grades = new_grades;
		}
	}
}
```

Of the 1000 calls to realloc, how often is the memory reallocated to another memory address?

The memory is never reallocated to another memory address, but it's memory size keeps expanding.


### Activity 10: Using a dynamically sized buffer

```c
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

/*
 * Reads the file "E.coli.txt" into a dynamically allocated array
 */
 

int main( void ) {
    char *ptr = NULL; // the memory address of the array
    size_t capacity = 20; // the capacity of the array

    printf("Loading... \n");

    const size_t capacityOGValue = capacity;
    size_t count = 0; // the number of actual values stored in the array

    ptr = realloc(ptr, sizeof(char[capacity])); // allocate memory

    if (ptr == NULL){ // check if allocation worked
        fprintf(stderr, "Memory allocation failed\n");
        printf("Memory allocation failed\n");
        return 1;
    }

    // open the file "E.coli.txt" for reading in text mode
    FILE *file = fopen("E.coli.txt", "r");

    if (file == NULL) { // check if file was opened
        fprintf(stderr, "Error opening file\n");
        return 1;
    }

    int c = fgetc(file); // read next character from file

    while (c != EOF) {

        if (count == capacity){

            char *ptr_ = realloc(ptr, sizeof(char[capacity]) * sizeof(char[capacityOGValue]) * (count / sizeof(char[capacityOGValue]))); // allocate memory
            //(count / sizeof(char[capacityOGValue])) is the amount of times the ptr size has been multiplied.

            capacity = capacity + capacityOGValue;



            if (ptr_ == NULL){ // check if allocation worked
                free(free);
                fprintf(stderr, "Memory allocation failed\n");
                return 1;
            }
            else{
                ptr = ptr_;
            }

        }

        ptr[count++] = (char) c; // store current character, then increase count
        c = fgetc(file); // read next character from file


    }

    // count how many 'a's are in the file
    int freq = 0;
    for (size_t i = 0; i < count; ++i) if (ptr[i] == 'a') freq++;

    printf("Character 'a' appears %d times in the array - Expected amount: 1142069\n", freq);

    // let the program crash if the frequency is not correct
    assert(freq == 1142069);

    free(ptr); // release the memory
}
```

## Looking back

### What we've learnt

We've learned the concepts of Stack and Heap, variable lifetime, and how to use malloc, realloc, and free functions. 

### What were the surprises

Dynamic memory is a surprising concept we learned. 

### What problems we've encountered

We had a hard time understanding what the activties what us to do.

### What was or still is unclear


