Understanding C Pointer Fundamentals - With Examples

September 16, 2013 | By
| Reply More

A pointer is a variable that points to a memory location by storing its memory address. Though a pointer can point to any address but generally the memory address stored by a pointer is that of a program variable (local or global) or that of a memory chunk on heap (i.e., dynamic memory). Pointers not only let you play with the memory addresses but also let you change the value kept at these memory addresses. In this article we will discuss the basics of pointers in C.

Pointer Syntax

A pointer can be declared as :

[type-of-pointer] *[name-of-pointer]

In the syntax above:

  • The type-of-pointer usually refers to the type of variable the pointers points. For example, if the pointer points to a character variable then the type-of-pointer is char.
  • The asterisk (*) character distinguishes a pointer declaration from the declaration of other variables in C language.
  • Just like every variable has a name, The name-of-pointer is the name of pointer variable.

Here is an example of a pointer declaration :

char *ptr

If you compare the above declaration with the syntax we just discussed, you will observe that the type-of-pointer here is char and ptr is the name-of-pointer. In short, you can say that ptr is a character pointer. This means that the pointer ptr can store the address of a character variable.

A Point Worth Noting

We have just discussed the syntax of pointer declaration. Did you notice the space between type-of-pointer and the asterisk (*) ? It is not mandatory to have this space here, you can associate * with type-of-pointer and insert a space between * and name-of-pointer.

For example, instead of char *ptr, you can also write char* ptr. There is nothing wrong in this but it's not a good practice to follow. Why? Here is an example.

Suppose you want to declare multiple character type pointers. So you can do this in the following way:

char *ptr1, *ptr2, *ptr3

This is correct because you did not forget to associate * with ptr1, ptr2 and ptr3. But if you are used to associating * with pointer type then you can end up doing something like :

char* ptr1, ptr2, ptr3

This is wrong because if you go by this declaration then ptr1 gets declared as a pointer but ptr2 and ptr3 are declared as character variables. So, it is always advised to follow the good practice of associating the asterisk (*) with the pointer variable name rather than its type.

Assigning Address And Retrieving Value

After you have understood the syntax, the next step is to understand how to assign memory address to a pointer and how to retrieve the value kept at that memory address using the same pointer.

Here is how a pointer can be made to point to a variable:

char ch = 'a';
char *ptr = &ch

Observe the second line the example above, a character pointer ptr was assigned with the memory address of character ch by applying the ampersand (&) operator to ch.

Since pointer ptr now holds the memory address of the variable ch, to access the value kept at this address just use asterisk (*) along with ptr.

Here is an example :

char ch = 'a';
char *ptr = &ch;
printf("\n The value pointed by ptr is [%c]\n", *ptr);

So, the printf statement would print the value a as *ptr now refers to the value of the variable ch.

Null Pointers

Just like any other variable, never use a pointer without assigning any value to it.

For example :

char *ptr;
printf("\n The value pointed by ptr is [%c]\n", *ptr);

The printf statement (shown above) will most likely crash the program because it tries to access the value kept at some garbage address (as ptr is not pointing to a valid memory location). So it is always important to initialize a pointer with NULL and always check the pointer before using it.

Here is the recommended way of doing the same :

char *ptr = NULL;
if(NULL != ptr)

{
printf("\n The value pointed by ptr is [%c]\n", *ptr);
}
else
{
// Throw any error or warning
}

To conclude, a pointer that is initialized with NULL is known as a NULL pointer.

Dangling Pointers

One of the most popular use of pointers in C is to store addresses of dynamic memory that get allocated through malloc() function. Here is an example :

char *ptr = (char*)malloc(10);

So you can see that the starting address of the 10 bytes (allocated through malloc()) is held by pointer ptr. Now, to free this memory, the free() function is used.

Here is an example :

char *ptr = (char*)malloc(10);
...
...
...
free(ptr);

The free() function makes sure that the memory is freed and is available for further use but it does not clear the memory address pointed by pointer ptr. This means that the pointer ptr still holds or points to the starting address of the 10 byte dynamic memory chunk that was freed using free() function.

The pointers which point to a memory that is already freed are known as dangling pointers. Dangling pointers are dangerous because any usage of these pointers may cause the program to crash. The best practice is to make a pointer point to NULL immediately after the memory (which it points to) is freed and check the pointer (for NULL value) wherever it is used next.

Here is an example :

char *ptr = (char*)malloc(10);
...
...
...
free(ptr);
ptr = NULL;

Pointer Arithmetic

Depending upon the type of pointer, it can traverse forward and backward. This means, for example, if a character pointer ptr points to the first byte of a 10 byte chunk of memory then we already know that the value at first byte can be accessed through *ptr. This is because ptr represents the address of first byte and by applying * you get the value kept at that address.

Now, to access the value at the second byte, just do *(ptr+1). So you can see that ptr was incremented with 1 (ie memory address held by ptr + 1) and then the value operator (*) was applied. Similarly, *(ptr+2), *(ptr+3) ... *(ptr+9) can be used to access values kept at the respective bytes.

A point worth re-iterating is that a pointer always gets incremented with the default size of its type. For example, if ptr would have been an integer pointer (assuming default size of an integer as 4 bytes) then ptr+1 would have refereed to the 5th byte ie first byte address (to which ptr already points) + 4.

NOTE - Expression like ptr[0], ptr[1], ptr[2] etc can be used in place of *ptr, *(ptr+1), *(ptr+2) etc.

A Complete Working example

Here is a dummy example that shows all the aspects of pointers that we have discussed till now :

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

#define SIZE 10

int main(void)
{
char *ptr1, *ptr2, *ptr3;
ptr1 = ptr2 = NULL;

char ch = 'a';
ptr1 = &ch;

ptr2 = (char*)malloc(SIZE);
memset(ptr2,0,SIZE);

ptr3 = (char*)malloc(SIZE);
memset(ptr3,0,SIZE);

printf("\n Enter a string (less than 10 characters)\n");
fgets(ptr2, SIZE-1, stdin);

strncpy(ptr3, ptr2, SIZE);

ptr2[0] = *ptr1;

if(0 == strncmp(ptr3, ptr2, SIZE))
{
printf ("\n The string is still the same \n");
free(ptr2);
ptr2 = NULL;
}

if(NULL != ptr2)
{
printf("\n The new string is [%s]\n",ptr2);
}

return 0;
}

And here are a couple of outputs of this program :

$ ./pointers

Enter a string (less than 10 characters)
expert

The new string is [axpert
]

$ ./pointers

Enter a string (less than 10 characters)
anexpert

The string is still the same

Filed Under : HOWTOS

Free Linux Ebook to Download

Leave a Reply

Commenting Policy:
Promotion of your products ? Comment gets deleted.
All comments are subject to moderation.