Pointers

C Language

Introduction

In this lesson, we are going to discuss the most difficult concept in C Programming: pointers. Pointers is very convenient and useful, but also is the subject that most bugs in a program occurs. To understand pointers throughly, you must read a lot of example programs and practice with it. Pointer is a type of variable unique to C.


Pointers

Pointers are variables that stores addresses. Remember we compared variables in the computer memory to boxes, with each box with a box number, and the value in that variable is the things put in the box. Pointers are variables that store addresses (or box numbers) of other variables. In the computer memory, each variable takes up certain amount of space in the memory, and they are stored in cells in the memory. Each cell has an address, and the computer access the value in that cell through this address. Another way of thinking of it is: if you have something locked in a drawer and you want to get it, you can either directly use your key to open the drawer and get it (which is what you have done up until now, using a normal variable); or, another safer way of doing so is that you can lock your key in another drawer, and you open that drawer, take that key, and open your drawer this way (using pointers).Pointers are variables that stores these addresses. Here is a diagram for a pointer variable:

In this diagram, the computer memory is shown as a long strip of cells, each cell with its address. Here, for demonstration, we just used address 2000 to 2009 (an actual memory address would be much longer, and is often in hexadecimal form). In the first four cells, an integer variable was stored with a value of 15; in address 2004, the value 'C' was stored; and in cells 2005-2008, a float 2.8932 was stored. At address 2009, we stored a pointer variable, holding the address of 2005, which was pointing to the float value of 2.8932.

Pointers are very important and useful because with pointers, you can manage and access more complex data; increase program efficiency; allocate memory dynamically; access arrays and strings much easier (especially strings); return multiple values in a function; and directly perform operations with memory addresses, etc.

In the sections below, we will frequently mention "the address the pointer is pointing to" and "the value of the address the pointer is pointing to", and many other statements like those, which may create misunderstandings if you just started programming. The former of the basic two means the value of the pointer, which is an address of what the pointer is pointing to; the later means that value stored in that address, if your pointer is pointed to variable num, then it means the value of num.

Here is the example program of the use of pointers (we will explain each part of it in our lesson), we will try to explain pointers as best as we could in this lengthy lesson:

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

void sort(int *a,int *b)
{
  int temp;
  if(*a>*b)
   {
     temp=*a;
     *a=*b;
     *b=temp;
   }
}

int main()
{
  int num=58;
  int *pointer_1; /*A pointer pointing to an integral variable*/
  int *pointer_2;
  pointer_1=&num; /*Assigns the address of num to pointer*/
  pointer_2=malloc(sizeof(int)); /*Dynamically allocate pointer_2 by reserving some memory for
 an integral value and pointing pointer_2 to it*/
  *pointer_2=6; /*Assigns 6 to the value of the address pointed by pointer_2, not the address*/
  printf("pointer_1 points to %p holds %d;\n",pointer_1,*pointer_1);
  /*First print the address in pointer_1, then print out the value in that address*/
  printf("pointer_2 points to %p holds %d.\n",pointer_2,*pointer_2);
  sort(pointer_1,pointer_2);
  printf("The sorted numbers: %d,%d.\n",*pointer_1,*pointer_2);
}

pointer_1 points to 0x7fff4996df3c holds 58;
pointer_2 points to 0x857010 holds 6.
The sorted numbers: 6,58.

Declaration

Pointers are a type of variable, so they need to be declared if you want to use them. The basic rules for variable declaration is same in C for any variable being:
variable-type variable-name;
Remember, pointers point to an address of the variable, and although you can change what the pointer points to throughout the program just by assigning it a value, it can only point to one type of variables (so a pointer pointing to integers cannot point to a character variable). So when declaring a pointer, the declaration is like this:
point-to-type *pointer-name;
For example, a pointer named p pointing to an integral variable declaration is like this:
int *p;
The asterisk indicates that it is a pointer, not just normal int p.

A pointer declaration is the same as declaration of other variables, and you can assign a initial value, declare multiple at once, and do whatever you can do to normal variables.

Here are some examples of pointer declarations:
int *input;
int *input,*output;
char *letter;
float *pi;
char *word;
int *input=&num; /*See Assignment below*/

Also, another thing to note is that when you are using functions dealing with pointers like the malloc() function we are going to discuss, it is safest to include another header file called <stdlib.h>, which stands for STanDard LIBrary.Headerfile, which includes a wide range of functions you can use. So at the top of your program, put #include <stdlib.h> after your normal headerfiles (#include <stdio.h>, etc) if you plan to use pointers.

After you declare a pointer variable, the computer will create a pointer variable which can point to the type you defined, and you will be able to use it.

Assignment and Access

Now you've created a pointer. In order to "point" it at a variable (assigns it an address, which leads to the variable), you need to assign it an address. Pointers are just the same as normal variables and cannot be used before you give them values. There are two basic operators in C dealing with memory:
* Pointer operator (or, get value)
& Address operator (or, get address)
The first one is used when you have an address already stored in a pointer, and wants to access the value in that address, you would use that operator. *address gives you the value in that address. While & is the opposite, it gives you the address of a variable. If you created a variable num, you can get its address in the computer by this expression: &num.

#include <stdio.h>
#include <stdlib.h> /*Although we are not using the functions in stdlib.h, it is a good habit
to still include that when you use pointers*/

int main()
{
  int num; /*Create a normal integer variable*/
  int *pointer; /*Create a pointer pointing to integral addresses*/
  pointer=&num; /*Points pointer to num by assigning the address of num to pointer*/
  num=5; /*Assigns 5 to num*/
  *pointer=6; /*pointer is pointing to the address of num, which currently holds the value of 5.
With the pointer operator '*', the expression here (*pointer) means the value of the address stored
in pointer, which is equivalent to the value of num. So by this assignment, we changed the value
of num to 6.*/
  printf("The address of num is: %p;\nThe address in pointer is: %p.\n",&num,pointer);
  /*You can use %p to print an address*/
  printf("The value of num is: %d;\nThe value of *pointer is: %d.\n",num,*pointer);
}

The address of num is: 0x7fff1a2cfcf4;
The address in pointer is: 0x7fff1a2cfcf4.
The value of num is: 6;
The value of *pointer is: 6.

Here is an diagram illustration of the relationships between the two variables in this program with the pointer operators:

In this program, we created a integer variable num, and a pointer that points to a integer address named pointer. We then pointed or set the value of pointer to the address of num through the assignment pointer=&num;, remember, & gets the address of something, so &num gets the address of num, which in this execution is 0x7fff1a2cfcf4, and assigns it to pointer. Then, we set num to 5. After that, the assignment *pointer=6; set the value in the address pointer is pointing to to 6. pointer is currently pointed to 0x7fff1a2cfcf4, which is the address of num, and the pointer operator '*' gets the value inside that address (previously 5), which is equivalent to the value of num, and this assignment assigns 6 to that. As you can see through the printf statements, &num and pointer has equal values, and num and *pointer has equivalent values.

Remember, '*' means the value in an address; and '&' means the address of something. Relating back to the comparison to the keys and drawers. If we have one drawer that represent the variable num, the thing inside it would be its value: 5. Then, pointer would be another drawer. The value inside that drawer, is a "key", or the address of num that can lead to the first drawer. Therefore, when we used the expression *pointer, we are taking that key out and opening that drawer the key leads to, which is num. By opening that drawer with the key, we can access its value. *pointer is the value of the address the pointer is pointed to, and &variable is the address of that variable.

Examples

Let's look at some examples of basic uses of pointers.

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

int main()
{
  int num;
  int *pointer; /*Creates a pointer pointing an integral address*/
  pointer=&num; /*Points pointer to num's address*/
  
  *pointer=10; /*Assigns 10 to the variable pointer is pointing to, which is the address of num*/
  /*Since pointer is pointing to num, 
  (*pointer)
  is equivalent to
  num*/
  (*pointer)+=5; /*Increase num by 5*/
  printf("The value of num is: %d",num);
}

The value of num is: 15

/*
  In this program, we use a function to find the greatest and least value in an array demonstrating
how we can use pointers to return multiple values in a function.
  We achieve this by passing in pointers as arguments, and allowing the function to access the variables
in the main function through its address.
*/
#include <stdio.h>
#include <stdlib.h>

void find_greatest_least(int list [10],int *greatest,int *least)
{
  int i;
  (*greatest)=list [0]; /*Give initial values*/
  (*least)=list [0];
  for(i=0;i<10;i++)
  {
    if(list [i]>*greatest)
      {
        (*greatest)=list [i];
      }
    if(list [i]<*least)
      {
        (*least)=list [i];
      }
  }
  /*We don't need to return a value since the changes are directly made to the addresses*/
}

int main()
{
  int numbers [10]={23,39,-3,0,23,83,-389,299,30,293};
  int l,g; /*These variables stores the least and greatest numbers*/
  int *pl,*pg; /*Pointers to variables least and greatest*/
  pl=&l;
  pg=&g;
  find_greatest_least(numbers,pg,pl);
  printf("The greatest number in the list is: %d;\nThe least number in the list is: %d\n",g,l);
}

The greatest number in the list is: 299;
The least number in the list is: -389

Since a function can only return one return value, and any changes we make in the function to any arguments we pass in won't take any affect to the variable in the calling function since the function creates local variables, in order to get two or more results returned in one function, we used pointers. We passed the pointers as arguments to the function, and the pointers stored the addresses of two variables, g and l, and when we pass them to the function, although the function creates local variables to store these values, since we have the address of the variables g and l, when we make changes to (*greatest) and (*least), the function goes to that address and make changes in that address, which is the address of g and l in main. Therefore, we will be able to return multiple values from a function this way.

Pointers and Arrays

You can use pointers to make array operations much easier, especially in strings. By using the malloc() function we will discuss later, you will be able to create arrays and strings very conveniently. In this example, we are going to introduce the basic relationships between arrays and pointers.

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

int main()
{
  int numbers [10]={9,8,7,6,5,4,3,2,1,0};
  int pointer; /*We will use this pointer to point to elements in the array. Since this arrays has
integral elements, we declared an integral pointer*/
  pointer=numbers; /*An array variable actually stores the address of the first element in the array 
where it begins, so by this assignment, we pointed the pointer to the first element of the array*/
  printf("numbers [0]=%d; *pointer=%d\n",numbers [0],*pointer);
  pointer=&(numbers [1]); /*This is another way to assign the address of an element in the array*/
  printf("numbers [1]=%d; *pointer=%d\n",numbers [1],*pointer);
  pointer=numbers+2; /*Because the elements in an array is consecutive in the memory, this statement 
points pointer to the 3rd element in the array*/
  printf("numbers [2]=%d; *pointer=%d\n",numbers [2],*pointer);
  printf("numbers [3]=%d; *(numbers+3)=%d\n",numbers [3],*(numbers+3)); /*This is another way to 
indirectly access an element in the array. numbers+3 is the address of the 4th element, and by using * 
we can access its value*/
  pointer=numbers;
  printf("numbers [4]=%d; *(pointer+4)=%d\n",numbers [4],*(pointer+4));
}

numbers [0]=9; *pointer=9
numbers [1]=8; *pointer=8
numbers [2]=7; *pointer=7
numbers [3]=6; *(numbers+3)=6
numbers [4]=5; *(pointer+4)=6

In the above example, we had a pointer pointing to addresses of elements in the array. As you remember from the Functions lesson, when you pass an array as an argument to a function, unlike other variables, changes made in that function to this array will also take effect in the main function. This is because array variables hold the initial address (demonstrated by the assignment above: pointer=numbers) of that set of variables, and because it is indirect access (access through addresses), like pointers, the changes will take effect in the calling function.

Here is the table of common expressions used in pointers:

Expression Meaning
&variable The address of a variable
*pointer The value in the address stored in pointer (indirect access)
array The address where the array begins (element 0)
array+i The address of array [i] (element i)
*(array+i) Equivalent to array [i] (value of element i)

We demonstrated use of each of those in the above examples. Values in arrays can also be accessed through *(array+i) which will represent array [i]. In fact, if you access a value through array [i], the computer actually turns it to *(array+i) and access the value this way.

Strings

As we described in previous chapters, strings are a form of array, so you can use them with pointers just as you would with arrays. String operations can be simplified very much with pointers. Let's look at some examples and explanations.

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

int word_count(char *str) /*This asks for a pointer pointing to a character for argument, which
can be an array, just like we had int *pointer; for an array*/
{
  int i;
  int count=0;
  int status=0; /*Records in or out of a word: 1=IN; 0=OUT*/
  for(i=0;*(str+i)!='\0';i++) /*Iterate over the string*/
    {
        if((*(str+i)>='A' && *(str+i)<='Z') ||
           (*(str+i)>='a' && *(str+i)<='z')) /*If the current character is a letter*/
          {
            if(status==0)/*If status is currently OUT, and the current character is a letter,
           change status to IN*/
            {
              status=1;
              count++;
            }
          }
        else if(status==1) /*If status is currently IN, and the current character is not a letter,
        change status to OUT*/
          {
            status=0;
          }
    }
  return count;
}

int main()
{
  char sentence [100]="Pointers lesson in Abacles C Programming Tutorial";
  int words;
  words=word_count(sentence); /*Since arrays are pointers, we can pass this to word_count*/
  printf("%s: %d words\n",sentence,words);
}

Pointers lesson in Abacles C Programming Tutorial: 7 words

As you see in the above example, we demonstrated some basic operations you can do with pointers and strings. We created a simple word count program that uses pointers and count the amount of words in a string. As you see, strings are same as arrays, so a string you declare holds its first address. When our word_count function asks for a char *str, a pointer to a character, we can pass the string sentence in since it holds the first address, which is a address that points to a character. That is why we can simply pass sentence to char *str.

Then, to access each character in the string, same as arrays, we used *(str+i), which is equivalent to str [i]. As you see, string opeartions with pointers is exactly the same as the ones with arrays.

Allocating Memory

A significant strength of C is that you can allocate or "reserve" memory for your program to use very easily. With functions like malloc(), you can tell the computer to reserve a given amount of memory for you to easily use. Imagine memory like a hotel, with each cell in the memory a room. With the malloc function, you can reserve rooms for your program and variables to use. But one thing to be careful is that you will need to free() the memory you used when the program ends.

#include <stdio.h>
#include <stdlib.h> /*In order to use the malloc() function, you will need to include the stdlib.h*/
#include <string.h>
  
int main()
{
  char *sentence;
  sentence=malloc(sizeof(char)*100);
  /*This statement gives sentence the space of 100 characters in the memory, giving it to use. See 
  explanation below*/
  /*Operations with the string is the same*/
  strcpy(sentence,"Pointers lesson in Abacles C Programming Tutorial");
  printf("%s",sentence);
  free(sentence); /*This statement free up the memory space and returns the memory to the computer*/
}

	    Pointers lesson in Abacles C Programming Tutorial

Here we introduce the malloc() function, and also the sizeof() function. The malloc() function "reserves" the given amount of memory, so we took that much of the memory and made it available for us to use. The function returned a address pointing to the beginning of the space reserved, when we assigned the address to sentence, we gave sentence that amount of space in the memory. The sizeof() function gives you the memory something would take up. Because we want a string of 100 characters, it would take up 100 characters in the memory. To determine how much space char would take up, we used the sizeof() function, which returns how much space it would take. With this statement, we asked it how much space a char would take up, multiplied it by 100, which would give us the space of 100 characters. The free() function simply free the space after use. Make sure to do this in your program.

The malloc() function is very important in C. With malloc(), you can increase program efficiency and conveniently provide memory for your variables to use. Especially when you are creating an array, structure, or other data types such as these.

Functions and Pointers

You can pass pointers as arguments to functions simply as you would with other variables (point-to-type *name), and a function can also return a pointer as a return value. When you declare such a function, you just need to put:
int *function(parameters)
{ }
If you want a function returning a pointer pointing to an integer. Returning the value is the same.

Let's look at an example.

Example

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

/*Find greatest number in an array and return a pointer to it*/
int *find_greatest(int *list,int length)
{
  int *greatest=list;
  int i;
  for(i=0;i<length;i++)
    {
      if(*(list+i) > *(greatest))
        {
          greatest=list+i;
        }
    }
  return greatest;
}

int main()
{
  int numbers [10]={67,323,93,-32,2939,0,129,27,89,29};
  printf("The greatest number is %d\n",*(find_greatest(numbers,10)));
}

The greatest number is 2939

Arrays of Pointers

Sometimes you will need to store an array of addresses, where you will need an array of pointers, or pointer to pointer (since array stores an address). For example, you may need an array of strings, and since strings and arrays are pointers, you will be able to achieve that through pointers! (This is actually the same as a two-dimensional array, since arrays store addresses, but pointers makes it easier sometimes) Let's look at an example:

Examples

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

int main()
{
  char *days [7]={"Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"};
  /*The declaration above declares an array of 7 elements each containing a string, which 
  can be represented as a pointer to a character*/
  int input;
  printf("Input a number:");
  scanf("%d",&input);
  printf("%s\n",*(days+(input-1)));
}

Input a number:3
Wednesday

This next example is another version of the example above, since char *days [7] is an array of pointers, it can also be declared as char **days; This version is much more complicated, and we will discuss this a little further in the Extension lesson.

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

int main()
{
  char **days;
  int input,i;
  days=malloc(sizeof(char *)*7); /*The pointer days points to a pointer, therefore, sizeof(char *)*/
  for(i=0;i<10;i++)
    {
      *(days+i)=malloc(sizeof(char)*10); /*Initialize each string in the array*/
    }
  strcpy(*(days+0),"Monday");
  strcpy(*(days+1),"Tuesday");
  strcpy(*(days+2),"Wednesday");
  strcpy(*(days+3),"Thursday");
  strcpy(*(days+4),"Friday");
  strcpy(*(days+5),"Saturday");
  strcpy(*(days+6),"Sunday");
  printf("Input a number:");
  scanf("%d",&input);
  printf("%s\n",*(days+(input-1)));
  for(i=0;i<10;i++)
    {
      free(*(days+i));
    }
  free(days);
}
	

Input a number:3
Wednesday

We will discuss using pointers with other concepts as we introduce them in future lessons.


Practice

Write a program to solve an equation in form of ax^2 + bx + c = 0. The program should use a function with parameters a, b, and c to calculate the two solutions, and use pointers to return the results.

Write a program that uses the malloc() function to create an array of 100 characters. Allow the user to input and store it in the string, and write a function to count how many words are in the string.

Debug the following program:

#include <stdio.h>

/*This function counts how many sentences are in a string*/
int count_sntc(char string)
{
  int i;
  int count;
  for(i=0;i<250;i++)
   {
     if(string [i]=='.' || string [i]=='!' || string [i]=='?')
      {
        count++;
      }
   }
  return count;
}

/*Creates 5 strings each with 50 characters to be counted*/
int main()
{
  char **strings;
  int i;
  strings=malloc(sizeof(char)*250);
  for(i=0;i<5;i++)
    {
      printf("Enter string %d:",i+1);
      scanf("%s",strings+i);
    }
  for(i=0;i<5;i++)
    {
      pritnf("%d: %d sentences\n",i+1,count_sntc(strings+i));
    }
}

Write a program that include a function which reverses a string. If "This is a string." is inputted, the function should change it to ".gnirts a si sihT". Use pointers.

Challenge: write a program that uses char ** to create an array of strings 5 strings, each containing 300 characters. Then use char * to create a string of 20 characters. Allow the user to input 5 paragraphs, storing them in the array of strings. Then allow the user to input a search, storing it in the string of 20 characters. Have a function that when giving one of the 5 strings and the search, find the occurrence of that word in the paragraph.

Additional practice on pointers:

Activity 13.2

Common Pointer Expressions

Here is a list of commonly used pointer expressions and their meaning for reference. In the table, "a" is an integer (int a=5;) that stores 5; "b" is an array (int b [10];); and "c" is a two-dimensional array (int c [5][10];).

Expression Meaning Value
a The value in a 5
&a The address of a The address (for example: 0x7fff1a2cfcf4)
pointer (int *pointer=&a;) The address of a The address (for example: 0x7fff1a2cfcf4)
*pointer (int *pointer=&a;) The value in the variable a which the pointer points to 5
b, pointer (int *pointer=b;) The address of the first element The address
b [i], pointer [i] (int *pointer=b;) The value in element i The value
b+i, pointer+i (int *pointer=b;) The address of element i The address
*(b+i), *(pointer+i) (int *pointer=b;) The value in element i The value
c [i][j], *(*(c+i)+j), pointer [i][j] (int **pointer=c;), *(*(pointer+i)+j) The value in element [i][j] The value


<< Previous TOC Next >>