C Pointers and Memory Allocation
- Pointers are addresses.
- Pointer notation.
- Array notation.
- Don't return a pointer to a local variable.
- Null termination.
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.
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.
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* example; /* Declares a pointer to an int. */
Getting the address of a variable.
int something = 3;
example = &something. /* Makes example point to the address of something. */
Dereferencing a pointer
*example = 2; /* Sets the value of the data pointed to by example. */
The square-bracket array notation is really just a short cut to prevent you from having to do pointer arithmetic.
char array; /* array is a pointer to array. /*
array = 12; /* This is equivalent to *(array+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.
Undisciplined C programmmers often return pointers to character arrays from functions, without documenting how that pointer should be used. If you encounter one of these functions you know that one of the following must be true:
- 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++). A char* return type,
rather than a const char* return type is often an indication that this is the case, though C programmers often
forget to use the const keyword properly with character arrays.
Or if the function is allocating resources in some special way (particularly if it's not just a pointer to a character array), then a second function is generally provided to un-allocate the resource when it is no longer needed.
- The data pointed to by the pointer exists for the lifetime of an object or service, beyond just the lifetime of the function call. But it is still generally safer to copy the data immediately, for instance with strcpy(). A const char* return type, rather than a char* return type is often an indication that this is the case, though C programmers often forget to use the const keyword properly with character arrays.
- The data pointed to by the pointer was allocated automatically (on the "stack") 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.
So far we have assumed that character arrays are null-terminated, meaning that there is a 0 value byte after the last character of the text, though that byte is still inside the allocated memory. This is normal on most modern systems, and is assumed by many standard C functions such as strcpy(), but you will find some functions that do not use this principle. In this case, the function should return the length of the array, so you can avoid reading past the end, and/or should take a pre-allocated array and its length as parameters to the function.
For instance, 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.
int result = my_function(array, 10);
/* Remember array is the address of array. */
strncpy() is an example of such a function. though strcpy() assumes null-termination.
Copyright © Murray Cumming, Openismus GmbH.
This work is licensed under a Creative Commons License.