DEV Community

mikkel250
mikkel250

Posted on • Updated on

Pointer basics in C

Overview

Indirection

Pointers are very similar to the concept of indirection employed in everyday life. An example of indirection would be if you purchase a new mouse at work, and all purchases are handled by the purchasing department. You would call Jane in the purchasing department and ask her to order a new mouse for you. Jane then contacts the supplier and orders the mouse. You receive a new mouse, but you are not ordering it directly from the supplier yourself.

In programming languages, indirection is the ability to reference something using a name, reference, or container instead of the value itself. The most common form of indirection is the act of manipulating a value through its memory address.
A pointer provides an indirect means to access the value of a particular data item. A pointer is a variable whose value is a memory address. Its value is the address of another location in memory that can contain a value.

The compiler must know the type of data stored in the variable to which it points so that it knows how much memory is occupied, or how to handle the contents of the memory to which it points.
Every pointer must be associated with a specific variable type, and can be used to point only to variables of that type -- i.e. a pointer of type "pointer to int" can only point to variables of type int.

The concept of a pointer

Many modern languages provide automatic memory management for you, where the memory is allocated and then deleted in the background -- known as garbage collection. The C language does not do this for you, so whenever you create something in memory, you have to manually delete it as well.
A pointer is a variable that stores an address - it 'points' to a location in memory, which contains a value. The ampersand that is included when storing a variable in the scanf() function is a pointer - it points to the location in memory where the value is stored.

The pointer has a special syntax in C - the asterisk * is used to declare a pointer, and the ampersand is the address where the value is stored. For example:

int Var1 = 100;
int *ptr = &Var1;
Enter fullscreen mode Exit fullscreen mode

In the example above, the *ptr is a pointer declaration, and what is references is the address in memory where the value for Var1 is stored, which is declared using the ampersand in &Var1.

The image below is for C++, but is one of the simpler images I could find that express the relationship of a pointer to memory and values.
Image of a pointer's relationship to memory and the value that is stored

So, the pointer *ptr in the example and illustration above references only the address in memory where the value of the variable Var1 is stored. It does not point directly to the value in the variable. Another way to say this is that a pointer stores a value, but the value is an address in memory.
You can then access the value stored at that address using certain syntax.

Declaring pointers

By convention, there is a space between the asterisk and the name of the pointer variable, and the asterisk is omitted when dereferencing a variable, but these are optional.
It is best to initialize a pointer with some value when it is declared. If there is not yet a value for some reason, then assign it to a NULL value (the equivalent of zero for a pointer) so it is guaranteed that it does not point to anything in memory, which will prevent accidental overwriting of memory by using a pointer that does not point to anything specific. To use the NULL value, include the stddef.h library to your source file:

#include <stddef.h>
int * pnumber = NULL;

number = 10;
pnumber = num; // to reassign a pointer, the star is not necessary to use after it is declared. 
Enter fullscreen mode Exit fullscreen mode

'Address of' operator - the ampersand

If you want to initialize the pointer with the address of a variable already declared, use the ampersand &, followed by the variable name. int * newPointer = &myNum; could be read as 'create a pointer of type integer named newPointer and it is equal to the address of myNumber.'
Other caveats:
The the variable which is referenced by a pointer must have been created before the pointer is declared so that the compiler can allocate the space in memory (and thus the address) for the pointer to reference.
A pointer can be declared along with regular variables in a multi-variable declaration statement - there is nothing special about declaration. E.g. in float *pVal, fnum; only the first variable is a pointer, the second is a regular variable.
By convention, pointers start with the letter 'p' so that one can tell it is a pointer just by looking at the name.

Accessing pointers - the dereference operator *, the format specifier %p, and displaying the number of bytes

Use the indirection operator * to access the value of the variable a pointer references. This is known as the dereference operator. It can be used directly in code as if it were the value itself.

int number = 15;
int *pnumber = &number;
int result = 0;

result = *pnumber + 5; // result is equal to 15 + 5, and will store 20
Enter fullscreen mode Exit fullscreen mode

The format specifier for pointers is %p. This will return the value of the memory address that is stored in the pointer. You can also display the address of the pointer itself by using the 'address of' operator. E.g. if you wanted to display the pointer's address (remember, the pointer itself has an address in memory), the value, and the address the pointer references in memory using example above, it could be done as below:

int pointerValue = *pnumber;

//the (void*) is to get around a warning from the compiler
printf("the address in memory of the pointer itself: %p \n", (void*)&pnumber); 
printf("value of the pointer reference: %d or %d \n", pointerValue, *pnumber);
printf("address in memory that the pointer references: %p \n", pnumber);
Enter fullscreen mode Exit fullscreen mode

To display the number of bytes the pointer occupies, use the sizeof() operator.
You may get a compiler warning when using it this way. To get around the warning, you could cast the argument to type int as below:

printf("pnumber's size: %d bytes \n", (int)sizeof(pnumber));
Enter fullscreen mode Exit fullscreen mode

If you're not familiar with casting in C, it's pretty simple: you just declare the type you wish the output to be in parentheses in front of the operator, and the compiler will convert it if possible (i.e. an int can cast to a float but a string can't).

This covers the basics of pointers in the C programming language.

Top comments (0)