- Richard Feynman -
Chapter:C Dynamic memory
Contents
- 1 Meta information about this chapter
- 2 Chapter videos
- 3 C Dynamic memory
- 3.1 Description
- 3.2 Videos
- 3.3 Exercises pt I - using ints
- 3.4 Solutions
- 3.4.1 1. Allocate memory
- 3.4.2 2. Allocate 100000000 bytes
- 3.4.3 3. Free memory
- 3.4.4 4. Allocate memory for 10 integers
- 3.4.5 5. Allocate memory in functions
- 3.4.6 6. Allocate memory in functions with parameters
- 3.4.7 7. Fill the array with some data
- 3.4.8 8. Put these functions in a separate c file.
- 3.5 Exercises pt II - using our own datatypes
- 3.6 Solutions
- 3.7 Exercises pt III - mediafile functions (optional)
- 3.8 Solutions
- 3.9 Links
- 4 Chapter Links
Meta information about this chapter
Expand using link to the right to see the full content.
Introduction
There are cases when you simply don’t know how much memory you need until the program executes. Imagine you want to load a music file up into memory - it would be a rather silly program if you would only load file of a certain size.
Instead you would like to adjust the memory to the file to upload. To allocate memory allocate memory like this we need to use something called dynamic memory.
Purpose
In order to write useful and “real” programs we need dynamic memory.
Requirements
The student shall be familiar with the following concepts:
- Pointer
- Variable
- Type
Goal
The student shall be able use the functions: • calloc • malloc • realloc • free
Instructions to the teacher
Common problems
Chapter videos
All videos in this chapter:
See below for individual links to the videos.
C Dynamic memory
Description
Videos
C Dynamic memory (eng) (swe) (pdf)
Exercises pt I - using ints
Allocate memory
Write a program that allocates 1000 bytes, using malloc and stores a reference to that memory in a variable.
Note: your program now leaks memory
Allocate 100000000 bytes
Write a program that allocates 100000000 bytes x 10000, using calloc and stores a reference to that memory in a variable.
void *mem = calloc(1000, 100000000);
printf ("mem: %p\n", mem);
Note: You will most likely not be able to allocate this amount of memory. Make sure to detect a mem allocation failure.
Free memory
Fix the memory leak in (2).
Hint: Use the function free
Allocate memory for 10 integers
Write a program that allocates memory for 10 integers. We can think of this (a bit) like an array of 10 integers. Assign each of these integer (boxes) the value of its index in the array. Add code to deallocate this memory.
Allocate memory in functions
Rewrite the program in the previous exercise to do the allocation of memory in a separate function.
Allocate memory in functions with parameters
Rewrite the program in the previous exercise to do the allocation of memory in a separate function taking the size of the array as a parameter. The integer values shall all be set to 0.
Fill the array with some data
Write (and use) a function that stores data in the array (given as a parameter). It is hard (read: impossible) for this function to know how many integers there in the "array" pointed to be the parameter, so you need to add a parameter saying this as well.
Put these functions in a separate c file.
Put the code that allocates memoy in a separate c file. Write a header file for it. Your program need now include the header file.
Solutions
Expand using link to the right to see the full content.
1. Allocate memory
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
void *mem = malloc(1000);
return 0;
}
2. Allocate 100000000 bytes
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
void *mem = calloc(1000, 100000000);
printf ("mem: %p\n", mem);
return 0;
}
3. Free memory
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
void *mem = calloc(1000, 100000000);
printf ("mem: %p\n", mem);
if (mem!=NULL)
{
free(mem);
}
return 0;
}
4. Allocate memory for 10 integers
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
int *ints = calloc(10, sizeof(int));
int i;
if (ints==NULL)
{
exit(1);
}
for (i=0;i<10;i++)
{
ints[i] = i ;
}
free(ints);
return 0;
}
5. Allocate memory in functions
#include <stdlib.h>
#include <stdio.h>
int * allocate_ints()
{
return calloc(10, sizeof(int));
}
int main(void)
{
int *ints = allocate_ints();
if (ints==NULL)
{
exit(1);
}
free(ints);
return 0;
}
6. Allocate memory in functions with parameters
#include <stdlib.h>
#include <stdio.h>
int * allocate_ints(size_t nr)
{
return calloc(nr, sizeof(int));
}
int main(void)
{
int *ints = allocate_ints(10);
if (ints==NULL)
{
exit(1);
}
free(ints);
return 0;
}
7. Fill the array with some data
#include <stdlib.h>
#include <stdio.h>
int * allocate_ints(size_t nr)
{
return calloc(nr, sizeof(int));
}
int set_ints(int *ints, int nr)
{
int i;
if (ints==NULL)
{
return 1;
}
for (i=0;i<nr;i++)
{
ints[i] = i ;
}
return 0;
}
int main(void)
{
int *ints = allocate_ints(10);
if (ints==NULL)
{
exit(1);
}
set_ints(ints, 10);
free(ints);
return 0;
}
8. Put these functions in a separate c file.
Our "main file" (called 10-ints.c)
#include <stdio.h>
#include <stdlib.h>
#include "alloc-ints.h"
int main(void)
{
int *ints = allocate_ints(10);
if (ints==NULL)
{
exit(1);
}
set_ints(ints, 10);
free(ints);
return 0;
}
Our "alloc file" (called alloc-ints.c)
#include <stdlib.h>
int * allocate_ints(size_t nr)
{
return calloc(nr, sizeof(int));
}
int set_ints(int *ints, int nr)
{
int i;
if (ints==NULL)
{
return 1;
}
for (i=0;i<nr;i++)
{
ints[i] = i ;
}
return 0;
}
Our "alloc header file" (called alloc-ints.h)
int * allocate_ints(unsigned int nr);
int set_ints(int *ints, int nr);
You can check the source code (and download) here:
Exercises pt II - using our own datatypes
In some of the following exercises we refer to a struct called mediafile which you find here below:
#define SONG_SIZE 24
#define ARTIST_SIZE 24
#define ALBUM_SIZE 24
typedef struct mediafile_
{
char name[SONG_SIZE];
char artist[ARTIST_SIZE];
char album[ALBUM_SIZE];
} mediafile;
Allocate mem for 1 mediafile
Write a program that allocates memory for 1 mediafile, using calloc. The memory shall be stored in a varialbe.
Note: your program now leaks memory
Fix the mem leak
Fix the memory leak in the prvious exercise.
Hint: Use the function free
Add name, artist and song
In your code, add values for name
, artist
and album
in mediafile
memory pointed to by your variable. Print out the information bu using the pointer.
Hint: It is ok to truncate the values. That is, if as an example the song name is 100 charcters long we shall store only 24.
Realloc
Add code, in your program to allocates memory for one more mediafile, using realloc. Add values for name
, artist
and album
in the second mediafile. When you're writing this code you probably notice that you'll get lots of code doing the same thing - this is a clear sign that we need to reorganise our code a bit. So let's solve this task by:
- add a function taking one pointer (to a mediafile) and three strings as parameters
- use this function to set the values
- write a function that prints a mediafile
- use this function to both mediafiles out.
Solutions
Expand using link to the right to see the full content.
Allocate mem for 1 mediafile
Suggested solution:
#include <stdio.h>
#include <stdlib.h>
#define SONG_SIZE 24
#define ARTIST_SIZE 24
#define ALBUM_SIZE 24
typedef struct mediafile_
{
char name[SONG_SIZE];
char artist[ARTIST_SIZE];
char album[ALBUM_SIZE];
} mediafile;
int
main(void)
{
mediafile* mf = calloc(1, sizeof(mediafile));
return 0;
}
Fix the mem leak
Suggested solution (only the main function):
int
main(void)
{
mediafile* mf = calloc(1, sizeof(mediafile));
if (mf!=NULL)
{
free(mf);
}
return 0;
}
Add name, artist and song
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
* WARNING: this program contains some code samples on how to allocate
* nad use dynamic memory. There are no programs as the below.
*
* DO NOT USE THIS PROGRAM AS BASIS FOR ANYTHING :)
*
*
*/
#define SONG_SIZE 24
#define ARTIST_SIZE 24
#define ALBUM_SIZE 24
typedef struct mediafile_
{
char name[SONG_SIZE];
char artist[ARTIST_SIZE];
char album[ALBUM_SIZE];
} mediafile;
int
main(void)
{
mediafile* mf = calloc(1, sizeof(mediafile));
char *tmp;
if (mf == NULL)
{
exit(1);
}
tmp = strncpy(mf->name, "May it always be", SONG_SIZE);
if (tmp==NULL)
{
free(mf);
exit(1);
}
tmp = strncpy(mf->artist, "Bonnie 'Prince' Billy", ARTIST_SIZE);
if (tmp==NULL)
{
free(mf);
exit(1);
}
tmp = strncpy(mf->album, "Ease down the road", ALBUM_SIZE);
if (tmp==NULL)
{
free(mf);
exit(1);
}
printf ("%s | %s | %s\n",
mf->artist, mf->album, mf->name);
free(mf);
return 0;
}
Realloc
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
* WARNING: this program contains some code samples on how to allocate
* nad use dynamic memory. There are no programs as the below.
*
* DO NOT USE THIS PROGRAM AS BASIS FOR ANYTHING :)
*
*
*/
#define SONG_SIZE 24
#define ARTIST_SIZE 24
#define ALBUM_SIZE 24
typedef struct mediafile_
{
char name[SONG_SIZE];
char artist[ARTIST_SIZE];
char album[ALBUM_SIZE];
} mediafile;
/**
*
* Function to set a mediafile struct
*
*
*/
int
set_mediafile(mediafile *mf, char *artist, char *album, char *name)
{
char *tmp;
tmp = strncpy(mf->name, name, SONG_SIZE);
if (tmp==NULL)
{
return 1;
}
tmp = strncpy(mf->artist, artist, ARTIST_SIZE);
if (tmp==NULL)
{
return 1;
}
tmp = strncpy(mf->album, album, ALBUM_SIZE);
if (tmp==NULL)
{
return 1;
}
return 0;
}
/**
*
* Function to print a mediafile struct
*
*
*/
void print_mediafile(mediafile *mf)
{
printf ("%s | %s | %s\n",
mf->artist, mf->album, mf->name);
}
int
main(void)
{
mediafile* mf;
mediafile *tmp;
int ret;
/*
* Allocate memory for 1 mediafile
*
*/
mf = calloc(1, sizeof(mediafile));
if (mf == NULL)
{
exit(1);
}
/*
* Set the newly allocated memory to some values
*/
ret = set_mediafile (mf, "Bonnie 'Prince' Billy",
"Ease down the road",
"May it always be");
/* Check if it went ok */
if (ret!=0)
{
/* We failed... too bad. Time to exit
* free the memory first */
free(mf);
exit(1);
}
print_mediafile(mf);
/*
* Reallocate memory for 1 mediafile
*
* Store the resulting memory in tmp in case we fail (re)allocating
*/
tmp = realloc(mf, sizeof(mediafile)*2);
/* Check if we failed (re)allocating new memory */
if (tmp==NULL)
{
/* We failed... too bad. Time to exit
* free the memory first */
free(mf);
exit(1);
}
mf = tmp;
/*
* Set the newly allocated (extra) memory to some values
*/
ret = set_mediafile ((mf+1), "Bonnie 'Prince' Billy",
"Joy and Jubilee",
"Master and Everyone");
/* Check if it went ok */
if (ret!=0)
{
/* We failed... too bad. Time to exit
* free the memory first */
free(mf);
exit(1);
}
/* ...and print */
print_mediafile(mf);
print_mediafile(mf+1);
/* free the mem */
free(mf);
return 0;
}
Source available: two-mediafiles.c
Exercises pt III - mediafile functions (optional)
When looking at the exercises above where we had our mediafile handling code (set_mediafile
and print_mediafile
) in the same file as the main function which always did the same thing we were sure about the incoming data to the functions. Now we're going to make our code usable by others so we need to make our code more robust.
In short we're going to create a mediafile software module with functionality to:
- create or increase (in size) a list of mediafiles
- set the values of a mediafile
- be able to return a string representation of a mediafile
The code shall be robust:
- shall not crash when getting a NULL pointer as input
- return status
We shall write tests for our module:
- testing with valid data
- testing with garbage data
To do this we're going to introduce a small trick to make things a bit easier. Let's write a struct that hols:
- a list of mediafile (i e mediafile*)
- the size of the list
typedef struct medialist_
{
mediafile * list;
unsigned int size;
} medialist;
Using this struct we get the list and the size of it in one place.
We are going to add our code to:
- a c file
medialist.c
. - a header file
medialist.h
To test our code we will also write some test function in separate files. More on that later on.
Let's begin!
enum for our return codes
Put the following enum in the header file.
enum
{
MEDIALIST_OK,
MEDIALIST,
MEDIALIST_ALLOC_FAILED
} return_codes;
init_medialist
We should now write a function called init_mediafile
(see prototype below) with the following parameters/arguments:
-
medialist* mlist
- a pointer to a medialist
Since we want to write robust code we need to:
- check if
list
is NULL, if so returnMEDIALIST_INVALID_ARGUMENTS
The function shall:
- set the size to 0
- set the list (
list-list
) to NULL
Prototype for the function:
int
init_medialist(medialist *list);
You can test your code by compiling your file and test-media/test-initmedia.c
(download test-media/test-initmedia.c to a directory called test-media/
).
Compile like this:
$ gcc -pedantic -Wconversion -Wall -Werror -Wextra -Wstrict-prototypes -g medialist.c test-media/test-freemedia.c -o test-media/test-freemedia
Execute like this:
$ test-media/test-freemedia
Init medialist; OK
Free medialist; OK
Free bad medialist; OK
set_mediafile
We should now write a function called set_mediafile
(see prototype below) that has the following parameters/arguments:
-
medialist* mlist
- a pointer to a medialist -
unsigned int pos
- the position in the mediafile list (mlist->list
) -
char* artist
- the artist to set -
char* album
- the album to set -
char* song
- the song to set
Since we want to write robust code we need to:
- check if any of the arguments are NULL, if so return
MEDIALIST_INVALID_ARGUMENTS
- check if
mlist->list
is NULL, if so returnMEDIALIST_INVALID_ARGUMENTS
Prototype for the function:
int
set_mediafile(medialist* mlist, unsigned int pos, char* artist, char* album, char* song);
You can test your code by compiling your file and test-media/test-setmedia.c
(download test-media/test-setmedia.c to a directory called test-media/
).
Compile like this:
$ gcc -pedantic -Wconversion -Wall -Werror -Wextra -Wstrict-prototypes -g medialist.c test-media/test-setmedia.c -o test-media/test-setmedia
Execute like this:
$ test-media/test-setmedia
Adding first: OK
Setting first: OK
Invalid media: '(null)' 'BLA' 'Songies'
Setting first but bad arguments: OK
Invalid media: 'BLA' '(null)' 'Songies'
Setting first but bad arguments: OK
Invalid media: 'BLO' 'BLA' '(null)'
Setting first but bad arguments: OK
Can't set media in medialist that is NULL
Setting first but bad arguments: OK
All set media tests passed
Note: the last line says that all tested passed.
add_mediafile
We should now write a function called add_mediafile
(see prototype below) that has the following parameters/arguments:
-
medialist* mlist
- a pointer to a medialist -
char* artist
- the artist to set -
char* album
- the album to set -
char* song
- the song to set
Since we want to write robust code we need to:
- check if any of the arguments are NULL, if so return
MEDIALIST_INVALID_ARGUMENTS
- check if
mlist->list
is NULL, if so returnMEDIALIST_INVALID_ARGUMENTS
The function shall increase the size of the list inside the medialist variable. After successfully increased the size the data shall be added to the last mediafile element in the list.
Prototype for the function:
int
add_mediafile(medialist* mlist, char* artist, char* album, char* song);
You can test your code by compiling your file and test-media/test-addmedia.c
(download test-media/test-addmedia.c to a directory called test-media/
).
Compile like this:
$ gcc -pedantic -Wconversion -Wall -Werror -Wextra -Wstrict-prototypes -g medialist.c test-media/test-addmedia.c -o test-media/test-addmedia
Execute like this:
$ test-media/test-addmedia
Adding first: OK
Adding second: OK
Invalid media: '(null)' 'Alby2' 'Songy2'
Adding bad artist: OK
Invalid media: 'Artr2' '(null)' 'Songy2'
Adding bad album: OK
Invalid media: 'arti' 'Alby2' '(null)'
Adding bad song: OK
Media list NULL
Adding with bad medialist: OK
Adding 0 of 100: OK
Adding 1 of 100: OK
....
Adding 96 of 100: OK
Adding 97 of 100: OK
Adding 98 of 100: OK
Adding 99 of 100: OK
free media
Add, to medialist.c
and medialist.h
a function that frees all allocated memory.
int
free_medialist(medialist *mlist);
print media
Add a file media-print.c
and a corresponding header file.
The functions shall print (not stdout or stderr but to a user supplied FILE* (such as stdout)).
The following function shall print one mediafile to stream.
int
print_mediafile(mediafile *mf, FILE* stream);
The following function shall print all mediafiles in the medialist to stream.
int
print_medialist(medialist *list, FILE* stream);
Media file program
Write a program that communicates with the user (using stdout and stdin). The program shall populate ask questions like the below:
Artist: Lynyrd Skynyrd
Album: Nuthin' Fancy
Song: Am I Losin
The program shall continue reading from the user until the user presses ctrl-d (which forces a EOF (end of file)). After the user has entered media and pressed ctrl-d the program shall write all data to stdout.
Testing the media file program
If you want you can test your program manually. Of you could use a small script we've written. This script "replaces" the user.
You can see the script here: gen-media.sh (download)
Let's give it a try. To generate one "media"
$ ./gen-media.sh 1
Grateful dead
Greatest hits
Rich Fulcher love me
To generate three "media"
$ ./gen-media.sh 2
George Jones
December is the coldest month
Vince and Neil
Lynyrd Skynyrd
Mighty boosh
Tom Petty
November november
Rich Fulcher love me
Note: the script generates a blank ("") artist, album or song every now and then. This is done on purpose to make sure we can handle this.
To test your program with generated input you can (on bash) do the following:
$ ./gen-media.sh 100 | ./media-manager
Solutions
Expand using link to the right to see the full content.
enum for our return codes
enum
{
MEDIALIST_OK,
MEDIALIST_INVALID_ARGUMENTS,
MEDIALIST_ALLOC_FAILED
} return_codes;
init_medialist
Suggested solution:
int
init_medialist(medialist *mlist)
{
if (mlist==NULL)
{
return MEDIALIST_INVALID_ARGUMENTS;
}
mlist->size=0;
mlist->list=NULL;
return MEDIALIST_OK;
}
set_mediafile
Suggested solution:
int
set_mediafile(medialist* mlist, unsigned int pos, char* artist, char* album, char* song)
{
if ( mlist == NULL )
{
fprintf(stderr, "Can't set media in medialist that is NULL\n");
return MEDIALIST_INVALID_ARGUMENTS;
}
if (pos >= mlist->size)
{
fprintf(stderr, "Can't set media at pos %d\n", pos);
return MEDIALIST_INVALID_ARGUMENTS;
}
if (CHECK_VAR(artist) && CHECK_VAR(album) && CHECK_VAR(song))
{
mediafile *media = mlist->list;
strncpy((media+pos)->artist, artist, ARTIST_SIZE);
strncpy((media+pos)->album, album, ALBUM_SIZE);
strncpy((media+pos)->name, song, SONG_SIZE);
}
else
{
fprintf(stderr,"Invalid media: '%s' '%s' '%s'\n",
artist, album, song);
return MEDIALIST_INVALID_ARGUMENTS;
}
return MEDIALIST_OK;
}
add_mediafile
Suggested solution:
static int
realloc_mediafile(medialist *ml, unsigned int newsize)
{
mediafile *list = ml->list;
mediafile *tmp;
tmp = realloc(list, sizeof(mediafile)*newsize);
if (tmp==NULL)
{
return 1;
}
ml->list = tmp;
return 0;
}
int
add_mediafile(medialist* mlist,
char* artist, char* album, char* song)
{
int ret;
if (mlist==NULL)
{
fprintf(stderr, "Media list NULL\n");
return 1;
}
if (!(CHECK_VAR(artist) && CHECK_VAR(album) && CHECK_VAR(song)))
{
return 2;
}
ret = realloc_mediafile(mlist, mlist->size+1);
if (ret!=0)
{
return 1;
}
mlist->size = mlist->size + 1;
ret = set_mediafile(mlist, mlist->size-1, artist, album, song);
if (ret!=0)
{
mlist->size = mlist->size - 1;
return 2;
}
return 0;
}
free media
Suggested solution:
int
free_medialist(medialist *mlist)
{
if (mlist==NULL)
{
return MEDIALIST_INVALID_ARGUMENTS;
}
free(mlist->list);
mlist->list=NULL;
return MEDIALIST_OK;
}
print media
int
print_mediafile(mediafile *mf, FILE* stream)
{
if ( mf==NULL || stream==NULL )
{
return 1;
}
if ( CHECK_VAR(mf->artist) &&
CHECK_VAR(mf->album) &&
CHECK_VAR(mf->name) )
{
fprintf (stream, "media: '%s'", mf->artist);
fprintf (stream, " '%s' ", mf->album);
fprintf (stream, " '%s'\n", mf->name);
}
else
{
return 1;
}
return 0;
}
int
print_medialist(medialist *mlist, FILE* stream)
{
unsigned int i;
int ret;
if (mlist==NULL || stream==NULL )
{
return 1;
}
fprintf (stream, "--------------------- list -------------- \n");
for (i=0; i<mlist->size; i++)
{
ret = print_mediafile(mlist->list+i, stream);
if (ret!=0)
{
return 1;
}
}
return 0;
}
Media file program
Suggested solution, mediafile-reader.h
:
#include "medialist.h"
int
read_media_user(medialist *m);
Suggested solution, mediafile-reader.c
:
#include <stdio.h>
#include <string.h>
#include "medialist.h"
#include "mediafile-reader.h"
static int
read_from_user(char *prompt, char* storage, int max_size)
{
char *tmp;
printf("%s ", prompt);
tmp = fgets(storage,max_size,stdin);
/* If NULL, return */
if (tmp==NULL)
{
return -1;
}
/* Ok, NOT NULL*/
/* trim trailing newline */
if (storage[strlen(storage)-1]=='\n')
{
storage[strlen(storage)-1]='\0';
}
else
{
while (getc(stdin)!='\n')
{
printf(".");
}
return 2;
}
return 0;
}
int
read_media_user(medialist *ml)
{
char artist_buffer[ARTIST_SIZE];
char album_buffer[ALBUM_SIZE];
char song_buffer[SONG_SIZE];
int ret;
ret = read_from_user("Artist: ", artist_buffer, ARTIST_SIZE);
if (ret != 0 )
{
return ret;
}
ret = read_from_user("Album: ", album_buffer, ALBUM_SIZE);
if (ret != 0 )
{
return ret;
}
ret = read_from_user("Song: ", song_buffer, SONG_SIZE);
if (ret != 0 )
{
return ret;
}
return add_mediafile(ml,
artist_buffer,
album_buffer,
song_buffer);
}
Suggested solution, media-manager.c
:
#include <stdio.h>
#include "medialist.h"
#include "media-printer.h"
#include "mediafile-reader.h"
#define NR_OF_FAKED_MEDIA 10
int main(void)
{
int ret;
medialist list;
list.list=NULL;
list.size=0;
while (1)
{
ret = read_media_user(&list);
if (ret==-1)
{
break;
}
}
print_medialist(&list, stdout);
free_medialist(&list);
return 0;
}
Links
Chapter Links
Our C FAQ
External links
C Programming/Memory management (from the C Programming wikibook).