Names specified here
Name Description Notes Source Availability
aligned_alloc() Allocate memory for specific alignment (·) <stdlib.h> C11
calloc() Allocate initialised memory (·) <stdlib.h> C89 C90 C95 C99 C11
free() Release memory (·) <stdlib.h> C89 C90 C95 C99 C11
malloc() Allocate memory for all alignments (·) <stdlib.h> C89 C90 C95 C99 C11
realloc() Reallocate memory (·) <stdlib.h> C89 C90 C95 C99 C11

C supports the creation (allocation) and destruction (de-allocation) of objects directly under the control of the program, using functions in <stdlib.h>. Such objects have dynamic storage duration, and do not have names, so they can only be accessed by pointers. A dynamic object is fundamentally just an array of bytes, and a pointer to the start of such an array serves to identify the object.

The type of a dynamic object is a matter of interpretation. The memory-management functions use void * as the type for pointers that identify dynamic objects, so they can handle objects of any type. A pointer of type void * to a newly created object can be automatically converted into a pointer to the type of object it is intended to be used as. Similarly, when an object is to be destroyed or resized, a pointer of a specific type can be passed as an argument, and it will be automatically converted back to a void *.

Three of the functions only create dynamic objects, one only destroys, and one can both create and destroy. During program execution, a changing set of valid addresses for dynamic objects exist. A function that creates a dynamic object adds the address of that object to that set, while a function that destroys an object deletes its address from the set. Only valid addresses for dynamic objects can be submitted for deletion.

Allocating single objects

#include <stdlib.h>
void *malloc(size_t sz);
void *aligned_alloc(size_t aln, size_t sz);

malloc and aligned_alloc allocate sz bytes of memory, returning a pointer to the first byte. To allocate memory for a single object, apply the operator sizeof to its type to get the minimum number of bytes required by the type:

struct point {
  int x, y;
};

struct point *p1 = malloc(sizeof(struct point));
struct point *p2 = aligned_alloc(alignof(struct point), sizeof(struct point));

Because sizeof can also take an expression which doesn't get (fully) evaluated, you can safely dereference the variable to be used to hold the object's address:

struct point *p1 = malloc(sizeof *p1);

This obviates having to restate the type of the object, and it doesn't have to be changed if the variable's type changes.

The alignment of a pointer returned by aligned_alloc has a minimum of aln, which much be a supported alignment, and sz must be a multiple of it. The alignment of a pointer returned by malloc is suitable for all types.

If an allocation fails, NULL is returned. A program must check for this before using the pointer. It's good practice to write specific functions to allocate objects of specific types, so that allocation and initialization are kept together:

struct point *alloc_point(int x, int y)
{
  struct point *ptr = malloc(sizeof *ptr);
  if (ptr == NULL)
    return NULL;
  ptr->x = x;
  ptr->y = y;
  return ptr;
}

Dynamic arrays

An array is just a series of adjacent objects of identical type in memory. To allocate a dynamic array, just use malloc again, but multiply the size of the element type by the number of required elements:

struct point {
  int x, y;
};

struct point *parr = malloc(sizeof *parr * len);

If an allocation fails, NULL is returned. A program must check for this before using the pointer.

#include <stdlib.h>
void *calloc(size_t nmemb, size_t sz);

calloc allocates space for an array of nmemb elements, each of sz bytes, and then initializes all the bytes to zero.

struct point *arr = calloc(100, sizeof arr[0]);

calloc is useful in two cases:

  • when the number of elements times the element size is greater than SIZE_MAX, and
  • when you want zero-initialized memory for security reasons.

Otherwise, just use malloc, and multiply the element size by the array length. calloc does not generally help to zero-initialize fields of a structure; for some types, especially pointers and floating-point, zero is not necessarily represented by zeroed bytes.

#include <stdlib.h>
void *realloc(void *ptr, size_t sz);

Dynamic arrays can be resized. realloc takes a pointer to an existing object, and attempts to resize it to sz bytes. It then either invalidates the old pointer and returns a new one with the correct allocation, or simply adjusts the allocation of the existing pointer. If a new object is created, the contents of the old object is copied into it, as far as they overlap. realloc can be used to both increase and decrease the size of an array.

Special care is needed when using realloc, as the old object continues to exist if resizing fails; a pointer to the old object must be retained until it is known not to be valid:

// Create an array of 10 elements.
struct point *pt = malloc(10 * sizeof *pt);
...
// Resize for 100 elements.
void *tmp = realloc(pt, 100 * sizeof *pt);
if (tmp == NULL) {
  // Out of memory, but the array still exists with 10 members!
} else {
  pt = tmp;
}

The alignment of a pointer returned by calloc or realloc is suitable for all types. Note that realloc(NULL, sz) behaves like malloc(sz).

Deallocation

If a pointer to a dynamic object is lost, the object cannot be destroyed until program execution terminates. Such a program contains a memory leak:

// Waste a thousand bytes by not retaining the address.
malloc(1000);

{
  // Waste a thousand bytes by forgetting the address.
  void *p = malloc(1000);

  // We forget at the end of the block.
}
#include <stdlib.h>
void free(void *ptr);

free is used to deallocate the memory of a dynamic object. The object's address ptr will no longer belong to the set of valid dynamic object addresses. Note that free(NULL) does nothing.

It is an error to attempt to access a dynamic variable after it has been freed:

struct point {
  int x, y;
};

struct point p, *ptr = malloc(sizeof *ptr);

free(ptr);
p = *ptr;     // error
ptr->x += 10; // error

If an object is compound, its components cannot be independently destroyed:

struct point *ptr = malloc(sizeof *ptr);
free(&ptr->y); // error

Also, an object with a different storage duration cannot be destroyed dynamically. It is an error to free a non-dynamic variable:

int i;
int a[10];
div_t s;

free(&i);      // error
free(&a[5]);   // error
free(&a[0]);   // error
free(a);       // error
free(&s);      // error
free(&s.quot); // error

It is an error to attempt to free a dynamic variable twice. After the first deallocation, the variable does not exist:

int *ia = malloc(sizeof(int) * 10);

free(ia);
free(ia); // error

Remember that ia is not the dynamic variable, but happens to point to it. After the first free, its value has no meaning, because it no longer belongs to the set of valid addresses of dynamic objects.

Most of these errors will not be reported or even detected by the compiler! They all have undefined behaviour. In practice, they will most likely corrupt some memory, and then your program will appear to run normally for a while, until something tries to use the corrupted memory, crashing the program. Memory-debugging tools exist to help detect these errors at the moment they occur.

A trivial example

This (rather pointless) example shows a correct usage of a dynamic variable (an array of integers). The number of elements is determined at run time by the first program argument.

#include <stdlib.h>

int main(int argc, char *argv[])
{
  int *ia, i;
  int len = argc > 1 ? atoi(argv[1]) : 10;

  ia = malloc(sizeof *ia * len);
  if (!ia)
    abort(); // A bit drastic!

  for (i = 0; i < len; i++)
    ia[i] = i * i;

  free(ia);

  return 0;
}

A dynamic string example

Being a language mainly geared to efficiency and light-weight operation, C doesn't provide any particularly advanced string processing that you might expect from (say) an interpreted scripting language (geared towards convenience). You'd simply use arrays of characters with sensibly-chosen static sizes. Nevertheless, it is often necessary to process strings without having to choose a sensible maximum (because such a value cannot be forseen).

The strdup function below is often available on many platforms, although it is not required by any version of the ISO C Standard.

#include <stdlib.h>
#include <string.h>

char *strdup(const char *s)
{
  // Compute the required space.
  size_t len = strlen(s) + 1;

  // Allocate that amount of space, and check for failure.
  char *r = malloc(len);
  if (!r) return NULL;

  // Copy the data and return.
  return memcpy(r, s, len);
}

A dynamic string is allocated with a call such as strdup("Banana"), and can be released later with free.

A dynamic structure example

Consider a representation of a person as a structure type, including a dynamically allocated name, and a date-of-birth:

struct date {
  int day, month, year;
};

struct person {
  char *name;
  struct date dob;
};

Let's write two complementary functions to allocate and deallocate a structure, including its internal dynamic components. First, an allocation function:

#include <stdlib.h>

// We'll borrow this from above.
char *strdup(const char *);

struct person *make_person(const char *name,
                           int day,
                           int month,
                           int year)
{
  // Allocate space for the over-all structure.
  struct person *r = malloc(sizeof *r);
  if (!r)
    return NULL;

  // Make a copy of the name.
  r->name = strdup(name);
  if (!r->name) {
    free(r);
    return NULL;
  }

  r->dob.day = day;
  r->dob.month = month;
  r->dob.year = year;
  return r;
}

Note that, if the internal allocation for the name fails, everything allocated so far must be released before returning NULL to indicate failure. An alternative might be to continue initialising the rest of the structure, and leave name null, but a partial structure is rarely useful in practice.

Now for the deallocation function which, like free, accepts and silently ignores a NULL argument:

#include <stdlib.h>

void free_person(struct person *r)
{
  if (!r) return;
  free(r->name);
  free(r);
}

As the program develops, and the structure type grows, these two functions should be maintained together to reduce the risk of failing to allocate or release some structure members.

Structures with array tails

From C99, it is possible to have a structure type whose final member is an array of unspecified length. For example, our person representation above could be defined as:

struct person {
  struct date dob;
  char name[];
};

The structure can now be allocated all at once, with the name tacked onto the end, if we allocate sufficient additional space:

#include <stdlib.h>
#include <string.h>

struct person *make_person(const char *name,
                           int day,
                           int month,
                           int year)
{
  // Allocate space for the whole structure.
  size_t len = strlen(name) + 1;
  struct person *r = malloc(sizeof *r + len);
  if (!r) return NULL;

  // Assign fields.
  memcpy(r->name, name, len);
  r->dob.day = day;
  r->dob.month = month;
  r->dob.year = year;

  return r;
}

The de-allocation function is now simply:

#include <stdlib.h>

void free_person(struct person *r)
{
  free(r);
}

This might be preferable, as it makes allocation and de-allocation more ‘atomic’, but it might also make things more complicated if the name is likely to be resized. As a distinctly allocated object, that would have been easy, but now the whole structure has to be re-allocated, and so references to it would become invalid.


CHaR
Sitemap Supported
Site format updated 2024-06-05T22:37:07.391+0000
Data updated 1970-01-01T00:00:00.000+0000
Page updated 2022-06-17T21:43:05.000+0000