Pointers in C and C++ give us direct access to memory locations. One of the most common usages is to provide a pass by reference mechanism for function parameters. They are also used with dynamic memory allocation which enables creating memory spaces as needed during program execution. Freeing the memory explicitly when done using it is also the programmer’s job in these languages.
Also see the Arrays and Strings notes for material related to pointers and those structures.
Pointers
These are variables that hold memory addresses, providing indirect access to the referenced location.
- Declaration:
sometype *ptrvarname
, e.g.int *toi
orchar *args[]
-
ptrvarname
is a variable that contains a memory address, a number which refers to that raw memory location - That memory address in turn should contain a value of type
sometype
-
NULL
means "points at nothing" (#include <stdlib.h>
to getNULL
defined; it is in fact0
).
Operators for pointers
We use two operators in conjunction with pointers. The &
operator when applied to a variable returns that variable. This enables us the access the address of a variable in order to store it in a pointer variable. The *
operator is called the dereferencing operator; it returns value being pointed to, that stored in the memory location held by the pointer variable. The following code snippet demonstrates these basic operations. Both operators have unary operator precedence, but one tier below ()
and []
operations.
int a, *toa; // a is a plain int, toa is a pointer to (memory address of) an int
toa = &a; // toa contains the memory address of a -- its a "pointer to a"
printf("%d",*toa) // dereference above toa to get the underlying int and print it out
Pointers and parameter passing
- Pointers can be used to "encode" call by reference in C - C has no primitive call by reference
- Pass variable address to pass a pointer (reference) to the variable - must use * as part of function prototype and definition parameter
- use *var within function to get or change value being referenced
Swap example using pointers to pass by reference:
void swap(int * a, int * b)
// a and b are pointers to integers: addresses of memory locations containing ints
{
int temp = *a; // dereference a (follow the pointer) to get to underlying integer
*a = *b; // "*a =" follows the pointer to get the integer's location, puts what b points to there.
*b = temp;
}
int main()
{
int x = 10, y = 5;
swap(&x, &y); // pass addresses which are of type "int *"
printf("%d %d\n", x, y); // now 5, 10
}
Pointer arithmetic
+
,-
,+=
,-=
for other pointers or integers- Most often used on pointers that are arrays
- Doesn't add the actual number, it adds that number times
sizeof
the base type - e.g. for variable
int * p
, code "p+1
" will in fact generatep+4
-- 4 bytes ahead will sit the next 32-bitint
. -
ptr1 = ptr2
assignment works forptr1/2
of same type -
ptr1 == ptr2
etc makes sense to compare ptrs ("do they point to the same memory location?"), andptr == NULL
Pointers to pointers
- When passing a pointer to functions, if want pointer to change, must pass address of pointer (pointer to pointer).
- Gets confusing to keep track of "which level" you are on -- watch out!
Dynamic Memory Allocation in C
Real Programs need dynamic memory. Arrays we have used up to now must have fixed size from when program launched. Often we need to let program data storage grow unboundedly (up to available memory on the computer). The solution is dynamic memory allocation.
- C takes a low-level view of dynamic memory: allocate blocks and free blocks of memory
-- compare to Java/Python/etc where you just saynew
or invoke class constructor to allocate space for new objects, and a garbage collector frees for you - Dynamic memory is allocated on a heap (which is a huge dynamically growable array)
- Data persists until it is explicitly
free
d - Can return
char*
from functions, unlikechar[]
Memory Allocation functions
void * calloc (size_t numels, size_t sizeofel)
e.g. int * arr = calloc (10,sizeof(int)); // arr[0] .. arr[9] are fresh 0'd array elements after this statement
- Makes array of
numels
with each element having sizesizeofel
; initialize all to 0's -
calloc
returns a pointer to this new memory block - Return type is
void *
and by C type promotion rules its OK to assignvoid *
to any type of pointer - If
NULL
(empty pointer) is returned, no space was allocated - Alert: its very easy to
calloc
too little or too much space if you pass in incorrect sizes - Aside:
size_t
is C's data type for sizes of things (the result type ofsizeof
); it should be anint
that can hold up to the size of the machine's memory, so it will be an unsigned 32 or 64-bitint
depending on your machine. - Aside 2: in older C style the result of calloc etc would need to be typecast; this is no longer needed or recommended.
void * malloc (size_t size)
e.g. char * str = malloc(sizeof(char) * 10); // can copy a 9-character string into fresh str now
- Creates memory block of given total
size
- Returns pointer to start of memory
void * realloc (void *ptr, size_t size)
- Copies memory pointed to by
ptr
to new place with newsize
- Returns a pointer to this new copy; frees memory pointed to by
ptr
-
ptr
should be a previously *alloc
'd value -
size
is as in malloc, for calloc-style use a size (numels * sizeel
) - If new size is bigger, new space uninitialized
For all of the *alloc
functions above, if allocation fails the function returns null. We should explicitly check for this:
char * memory = malloc(400000);
if (!memory) // remember null == 0, 0 is false
printf("Failed to allocate the amount of memory you requested\n");
Deallocating memory
void free(void *
ptr
)
- deallocates the memory at address
ptr
-
ptr
must be a previously *alloc'd return value -
ptr
is no longer pointing to valid storage afterfree
- A primary source of errors in C code is either
free
ing memory you are still using, or notfree
ing memory you are finished with, and using up all your computer memory eventually (a leak) -- not a problem in Java/Python/etc since they have a garbage collector
Valgrind
-
valgrind
checks for memory leaks:malloc
'd (orcalloc
'd orrealloc
'd) storage that is notfree
'd - Also finds invalid memory reads and writes (ie, accessing past end of array)
- It reports "still reachable" when you forgot to call
free
but you could have - A report of "definitely lost" means you cannot call
free
because the pointer needed to be deallocated in a helper function, it can't be done in main. - imagine if the Chrome browser called
func()
1000 times a second: it would eat up all your memory and then crash - Your submitted programs must not have memory leaks!
- See the Valgrind.org quickstart guide for more information.