Chapter:C Dynamic memory

From Juneday education
Jump to: navigation, search


Contents

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:

  1. add a function taking one pointer (to a mediafile) and three strings as parameters
  2. use this function to set the values
  3. write a function that prints a mediafile
  4. 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 return MEDIALIST_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 return MEDIALIST_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 return MEDIALIST_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).

Books this chapter is a part of

Programming with C book

Book TOC | previous chapter | next chapter