C Pointers and Memory Allocation

Contents

Introduction

A great many 'C programmers' appear to be lacking a basic understanding of pointers and memory allocation. However, they are essential to programming in C, and are really quite simple and clearly defined. Therefore, I will describe the use of pointers and the memory allocation issues which you must bear in mind.

These notes apply to C++ as well, but I find it inconceivable that anyone could be programming in C++ without yet understanding these concepts.

Pointers are addresses

A pointer stores an address in memory. You will often need to pass around a pointer to some data rather than copying that data each time it is passed.

For instance, the value of a pointer may be 42435. That number is an address in the computer's memory which is the start of some data. We can dereference the pointer to look at or change the data.

Pointer notation

The notation for manipulating pointers is not obvious because it uses the asterisk (*) for two different purposes, and in C++ the ampersand (&) is used in an additional way (declaring by-reference function parameters). However, in C there are really only three ways in which you will use the asterisk and ampersand with pointers. You may need to refer back to these three examples for a while, but eventually you will find their use straightforward.

Declaring a pointer

int* piExample; // Declares a pointer to an int.

Getting the address of  a variable.

int iSomething = 3;
piExample = &iSomething. // makes piExample point to the address of iSomething.

Dereferencing a pointer

*piExample = 2; // Sets the value of the data pointed to by piExample.

Array Notation

The square-bracket array notation is really just a short cut to prevent you from having to do pointer arithmetic.

e.g.:

char chArray[5]; // chArray is a pointer to chArray[0].
chArray[2] = 12; // This is equivalent to *(chArray+2) = 12;

Don't forget that when you pass an array in to a function you are really just passing a pointer to its first element, so the function also needs to know how many elements there are.

Use the p prefix!

Pointer notation in C and C++ is very hard to read, particularly in C because you must declare the pointers at the start of your functions, often several pages of code away from where they are used. You can make your life a great deal easier simply by prefixing all pointer variable names with a p.

This technique is borrowed from "hungarian" notation. Opinions differ greatly about hungarian notation, but the use of the p prefix does not suffer from the same disadvantages, so try not be prejudiced against it.

e.g.:

int iBob = 1;
int* piTemp; // declare a pointer to an int

piTemp  = &iBob; // set the pointer to the address of an int.
*piTemp = 2; // put 2 in the address pointed to by the pointer, effectively making iBob =2.

Don't return a pointer to a local variable!

Bluffer C programmers are forever returning pointers to character arrays from functions. If you come across one of these functions you know that one of the following must be true:

  1. The data pointed to by the pointer was allocated dynamically within the function with malloc (or new in C++), and the function expects the caller to un-allocate the data dynamically with free (or delete in C++). This would be legal but is only likely if the function is allocating resources for you in some special way. In these cases a second function is generally provided to un-allocate the resource when it is no longer needed.
  2. The data pointed to by the pointer was allocated automatically within the function and was un-allocated automatically when the function returned. The data pointed to by the pointer may contain the correct data right now, but the system may decide to overwrite it at any time. Bluffer programmers think this is OK because their program compiles and it occasionally works.

When a bluffer C programmer has returned a pointer to a local variable from a function, the function should generally be re-written as so:

Callers of this function must allocate the return data before calling the function with the address of that return data. Normally this data is allocated and unallocated automatically simply by declaring a normal variable.

e.g.:

char chArray[10];
bool bTest = MyFunction(chArray, 10);
// remember chArray is the address of chArray[0]. I could have declared it as char pchArray[10] to avoid confusion.

The classic example is the use of the standard strcpy and strncpy functions. 

Copyright © Murray Cumming.

Creative Commons License
This work is licensed under a Creative Commons License.

Openismus | Impressum