Memory Management and the Standard Library: Exercises

See here how to set up a development environment to do these exercises.

  • You should complete the essential exercises if you want to claim you know how to program in C.
  • If you want to go further and make sure you understand everything, you can also complete the additional exercises.

Essential Exercises

  1. Working with pointers
  2. Working with pointers (2)
  3. Dynamic memory allocation with malloc
  4. Dynamic memory allocation with malloc (2)
  5. Dynamic memory allocation with malloc (3)
  6. Standard input and string comparison
  7. Sleeping and measuring execution time
  8. File I/O

Working with Pointers

Consider the following program:

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

int add(int a, int b) {
    return a + b;
}

int main(int argc, char **argv) {
    if(argc == 3) {
        int a = atoi(argv[1]);
        int b = atoi(argv[2]);

        printf("%d + %d = %d\n", a, b, add(a, b));
    }
    return 0;
}

Modify the function add and its invocation so that it takes two int pointer parameters. Examples of output:

./pointer 10 20
10 + 20 = 30

./pointer 154 -12
154 + -12 = 142

To check the correctness of your program, use a use a Linux distribution with check50 installed and write your solution in a file named pointer.c. In a terminal, with that file in the local directory, check with this command:

check50 -l --ansi-log olivierpierre/comp26020-problems/2024-2025/week3-c-pointers-stdlib/01-pointer

Working with Pointers (2)

With a linked list, the programmer uses pointer chains to link together data structures. In the example program below, a simple linked list with 3 elements is constructed then the value of the last element is printed:

#include <stdio.h>

/* Typedef struct forward declaration for the pointer member */
typedef struct s_list_member list_member;

typedef struct s_list_member {
    int value;
    list_member *next;
} list_member;

int main(int argc, char **argv) {
    list_member lm1, lm2, lm3;

    lm1.value = 1; lm1.next = &lm2;
    lm2.value = 2; lm2.next = &lm3;
    lm3.value = 3; lm3.next = 0x0;

    printf("third member value is: %d\n", lm3.value);

    return 0;
}

Modify the second parameter of the call to printf in order to print the value of the third element by using the first member lm1 and following the pointer chain leading to the value of lm3. The expected output is:

./pointer2
third member value is: 3

To check the correctness of your program, use a use a Linux distribution with check50 installed and write your solution in a file named pointer2.c. In a terminal, with that file in the local directory, check with this command:

check50 -l --ansi-log olivierpierre/comp26020-problems/2024-2025/week3-c-pointers-stdlib/02-pointer2

Dynamic Memory Allocation with malloc

Write a program taking a list of integers as command line parameters, storing them in an array allocated with malloc, and sorting that array in increasing order.

Output examples:

./malloc 5 4 3 2 1
1 2 3 4 5 

./malloc 546 874 18 13 87 54 4651 54 877 8 46351 87 654 657 654
8 13 18 54 54 87 87 546 654 654 657 874 877 4651 46351

To check the correctness of your program, use a use a Linux distribution with check50 installed and write your solution in a file named malloc.c. In a terminal, with that file in the local directory, check with this command:

check50 -l --ansi-log olivierpierre/comp26020-problems/2024-2025/week3-c-pointers-stdlib/03-malloc

Dynamic Memory Allocation with malloc (2)

Write a C program that takes two integers as command line parameter, x and y, and prints on the standard output y lines of x integers corresponding to the first (x * y) natural integers. The numbers should be stored in a 2-dimensional array allocated with malloc before being printed.

# 3 rows, 4 columns
./malloc2 3 4
0 1 2 3
4 5 6 7
8 9 10 11

./malloc2 2 5
0 1 2 3 4
5 6 7 8 9

./malloc2 10 11
0 1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20 21
22 23 24 25 26 27 28 29 30 31 32
33 34 35 36 37 38 39 40 41 42 43
44 45 46 47 48 49 50 51 52 53 54
55 56 57 58 59 60 61 62 63 64 65
66 67 68 69 70 71 72 73 74 75 76
77 78 79 80 81 82 83 84 85 86 87
88 89 90 91 92 93 94 95 96 97 98
99 100 101 102 103 104 105 106 107 108 109

To check the correctness of your program, use a use a Linux distribution with check50 installed and write your solution in a file named malloc2.c. In a terminal, with that file in the local directory, check with this command:

check50 -l --ansi-log olivierpierre/comp26020-problems/2024-2025/week3-c-pointers-stdlib/04-malloc2

Dynamic Memory Allocation with malloc (3)

Fix the memory leak contained in the following program:

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

int main(int argc, char **argv) {
    int *a = malloc(10 * sizeof(int));
    if(!a) return -1;

    for(int i=0; i<10; i++)
        a[i] = i*2;

    int *b = a;

    for(int i=0; i<10; i++)
        printf("%d ", b[i]);
    printf("\n");

    return 0;
}

The expected output is:

./malloc3
0 2 4 6 8 10 12 14 16 18

To check the correctness of your program, use a use a Linux distribution with check50 installed and write your solution in a file named malloc3.c. In a terminal, with that file in the local directory, check with this command:

check50 -l --ansi-log olivierpierre/comp26020-problems/2024-2025/week3-c-pointers-stdlib/05-malloc3

Standard Input and String Comparison

Write a C program reading two strings from the standard input using fgets, and indicating if the strings are similar or not. Output examples:

./string
input string1:
test
input string2:
test
strings are similar

./string
input string1:
hello world!
input string2:
goodbye
strings are different

To check the correctness of your program, use a use a Linux distribution with check50 installed and write your solution in a file named string.c. In a terminal, with that file in the local directory, check with this command:

check50 -l --ansi-log olivierpierre/comp26020-problems/2024-2025/week3-c-pointers-stdlib/06-string

Sleeping and Measuring Execution Time

Write a C program taking an integer n as command line parameter and sleeping for n seconds. The execution time of the sleep function is measured and displayed. Examples output:

./time 3
sleep duration: 3.000082 seconds

./time 5
sleep duration: 5.000108 seconds

To check the correctness of your program, use a use a Linux distribution with check50 installed and write your solution in a file named time.c. In a terminal, with that file in the local directory, check with this command:

check50 -l --ansi-log olivierpierre/comp26020-problems/2024-2025/week3-c-pointers-stdlib/07-time

File I/O

Write a C program taking as command line parameter A) a file name f and B) a word w. The program then creates the file f-processed which is a copy of f where all occurrences of the word w have been deleted. Here is an example of execution:

cat sample-file-1
hello world
this is a test file containing the word hello several times
some lines do not contain that word
while others do: hello

./file sample-file-1 hello

cat sample-file-1-processed
 world
this is a test file containing the word  several times
some lines do not contain that word
while others do: 

You download sample-file-1 here.

To check the correctness of your program, use a use a Linux distribution with check50 installed and write your solution in a file named file.c. In a terminal, with that file in the local directory, check with this command:

check50 -l --ansi-log olivierpierre/comp26020-problems/2024-2025/week3-c-pointers-stdlib/08-file

Make sure that sample-file-1 is in the current directory alongside file.c

Additional Exercises

  1. Working with pointers (3)
  2. Working with pointers (4)
  3. Dynamic memory allocation with malloc (4)
  4. Dynamic memory allocation with malloc (5)
  5. String manipulation
  6. Copying data in memory with memcpy
  7. Math operations
  8. String to integer conversion with strtol
  9. Stream-based file I/O

Working with Pointers (3)

Write a program that takes an integer as parameter and places it in a variable of type int. The program then proceeds to print the value as well as the address of the variable as follows:

./pointer3 5
Variable contains 5 and is located @0x7ffcc6d1d7fc

./pointer3 93
Variable contains 93 and is located @0x7fffec3b3dfc

Printing pointers. Pointer can be printed in hexadecimal and prefixed with 0x using the %p format specifier for printf.

To check the correctness of your program, use a use a Linux distribution with check50 installed and write your solution in a file named pointer3.c. In a terminal, with that file in the local directory, check with this command:

check50 -l --ansi-log olivierpierre/comp26020-problems/2024-2025/week3-c-pointers-stdlib/09-pointer3

Working with Pointers (4)

Consider the following program printing a string to the standard output character by character:

#include <stdio.h>

int main(int argc, char **argv) {
    char *string = "hello, world!\n";

    int i = 0;
    while(string[i] != '\0')
        printf("%c", string[i++]);

    return 0;
}

Alter the loop to use a char * pointer as the iterator and as the way to access characters within the string for printing. The source code should contain no square bracket. The expected output is:

./pointer4
hello, world!

To check the correctness of your program, use a use a Linux distribution with check50 installed and write your solution in a file named pointer4.c. In a terminal, with that file in the local directory, check with this command:

check50 -l --ansi-log olivierpierre/comp26020-problems/2024-2025/week3-c-pointers-stdlib/10-pointer4

Dynamic Memory Allocation with malloc (4)

Write a C program using malloc to allocate an array able to contain 10 int, fill this array with the 10 first natural number (starting with 0). The expected output is:

./malloc4
0
1
2
3
4
5
6
7
8
9

To check the correctness of your program, use a use a Linux distribution with check50 installed and write your solution in a file named malloc4.c. In a terminal, with that file in the local directory, check with this command:

check50 -l --ansi-log olivierpierre/comp26020-problems/2024-2025/week3-c-pointers-stdlib/11-malloc4

Dynamic Memory Allocation with malloc (5)

Consider the following program:

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

void *my_realloc(void *ptr, size_t old_size, size_t new_size) {
    /* complete here */
}

int main(int argc, char **argv) {
    /* first malloc space for 5 int */
    int *array = malloc(5 * sizeof(int));
    if(!array) return -1;

    for(int i=0; i<5; i++) {
        array[i] = i*10;
        printf("before realloc, array[%d] = %d\n", i, array[i]);
    }

    /* expand the size to 10 int */
    array = my_realloc(array, 5*sizeof(int), 10*sizeof(int));
    if(!array) return -1;

    for(int i=5; i<10; i++)
        array[i] = i*10;

    for(int i=0; i<10; i++)
        printf("after realloc, array[%d] = %d\n", i, array[i]);

    free(array);
    return 0;
}

Write the function my_realloc that changes the size of a buffer previously allocated with malloc while preserving all or part of the buffer content according to the requested size. The function parameters are:

  • ptr: buffer address
  • old_size: current size of the buffer
  • new_size: new size requested

The expected output is:

before realloc, array[0] = 0
before realloc, array[1] = 10
before realloc, array[2] = 20
before realloc, array[3] = 30
before realloc, array[4] = 40
after realloc, array[0] = 0
after realloc, array[1] = 10
after realloc, array[2] = 20
after realloc, array[3] = 30
after realloc, array[4] = 40
after realloc, array[5] = 50
after realloc, array[6] = 60
after realloc, array[7] = 70
after realloc, array[8] = 80
after realloc, array[9] = 90

Memory copy in C. Memory copy is achieved with the memcpy function that takes 3 arguments: the destination address, the source address, and the number of bytes to copy. To use it you'll need to #include <string.h>. See here for more information.

Realloc. Although this functionality already exists in the form of the standard function realloc (see here -- for the sake of the exercise it is asked to implement it manually here.

To check the correctness of your program, use a use a Linux distribution with check50 installed and write your solution in a file named malloc5.c. In a terminal, with that file in the local directory, check with this command:

check50 -l --ansi-log olivierpierre/comp26020-problems/2024-2025/week3-c-pointers-stdlib/12-malloc5

String Manipulation

Write a C program reading a string from the standard input and capitalise the first letter of each word.

./string2
input a string:
we swears, to serve the master of the precious
We Swears, To Serve The Master Of The Precious

./string2
input a string:
and following our will and wind we may just go where no one's been
And Following Our Will And Wind We May Just Go Where No One's Been

Capitalising letters. See the toupper function: https://linux.die.net/man/3/toupper

To check the correctness of your program, use a use a Linux distribution with check50 installed and write your solution in a file named string2.c. In a terminal, with that file in the local directory, check with this command:

check50 -l --ansi-log olivierpierre/comp26020-problems/2024-2025/week3-c-pointers-stdlib/13-string2

Copying Data in Memory with memcpy

Write a C program taking an integer n as command line parameter, allocating an array of integer of size n, and filling that array with random integers -- each between 0 and 100. Next, a second array of size n is created and the content of the first array is copied into the second one with a single call to memcpy. Finally, both arrays are printed. Example output:

./memcpy 10
array1: 32 32 54 12 52 56 8 30 44 94
array2: 32 32 54 12 52 56 8 30 44 94

./memcpy 15
array1: 32 32 54 12 52 56 8 30 44 94 44 39 65 19 51
array2: 32 32 54 12 52 56 8 30 44 94 44 39 65 19 51

Random Numbers. See the rand function: https://linux.die.net/man/3/rand. For example to get a random integer between 0 and 9 (included): int random_int = rand()%10.

To check the correctness of your program, use a use a Linux distribution with check50 installed and write your solution in a file named memcpy.c. In a terminal, with that file in the local directory, check with this command:

check50 -l --ansi-log olivierpierre/comp26020-problems/2024-2025/week3-c-pointers-stdlib/14-memcpy

Math Operations

Write a C program reading a double with scanf and asking the user if he wants this number to be floored or ceiled. Next, the program performs the requested operation and displays the result. Output examples:

./math
Input a number:
12.4
Input 0 for ceil, 1 for floor
0
13.000000

./math
Input a number:
45.87
Input 0 for ceil, 1 for floor
1
45.000000

To check the correctness of your program, use a use a Linux distribution with check50 installed and write your solution in a file named math.c. In a terminal, with that file in the local directory, check with this command:

check50 -l --ansi-log olivierpierre/comp26020-problems/2024-2025/week3-c-pointers-stdlib/15-math

String to integer conversion with strtol

The program strtol.c converts a string entered by the user into an integer and prints it on the standard output. The conversion is realised with atoi, and as such it is not robust in case of malformed strings as well as under/overflows.

Modify the implementation of the function convert_and_print in this program to use strtol for the conversion rather than atoi, and make the program more robust against improper inputs. Output examples:

$ ./strtol
please enter an integer number (base 10): 1234
you have entered: 1234

$ ./strtol
please enter an integer number (base 10): foo
invalid string

$ ./strtol
please enter an integer number (base 10): 100000000000000000000
under/overflow

$ ./strtol 
please enter an integer number (base 10): -100000000000000000000
under/overflow

To check the correctness of your program, use a use a Linux distribution with check50 installed and write your solution in a file named strtol.c. In a terminal, with that file in the local directory, check with this command:

check50 -l --ansi-log olivierpierre/comp26020-problems/2024-2025/week3-c-pointers-stdlib/16-strtol

Stream-Based File I/O

This is a variation of a previous exercise targeting file I/O. The goal is similar: write a C program taking as command line parameter A) a file name f and B) a word w. The program then creates the file f-processed which is a copy of f where all occurrences of the word w have been deleted. This time, you should use the stream-based file I/O functions (fopen, fread, and fwrite) to write the program.

Here is an example of execution:

cat sample-file-1
hello world
this is a test file containing the word hello several times
some lines do not contain that word
while others do: hello

./stream sample-file-1 hello

cat sample-file-1-processed
 world
this is a test file containing the word  several times
some lines do not contain that word
while others do: 

You download sample-file-1 here.

To check the correctness of your program, use a use a Linux distribution with check50 installed and write your solution in a file named stream.c. In a terminal, with that file in the local directory, check with this command:

check50 -l --ansi-log olivierpierre/comp26020-problems/2024-2025/week3-c-pointers-stdlib/17-stream

Make sure that sample-file-1 is in the current directory alongside stream.c