San Diego State University logo
ME 203

Module 6 - Pointers


An Introduction to Pointers - How Pointers Work
Pointers and memory management are related topics. We're going to start with pointers in this section, and in Module 7 show how pointers, in conjunction with memory allocation via the malloc function, can handle situations where other programming techniques won't work.

Take a look at the following example;

01  #include <stdio.h>
02 
03  main() {
04    int vali;
05    vali = 5;
06    printf("Integer = %d", vali);
07  }

In the above code, line 4 tells the CPU to find some place in the computer's memory, and set aside 4 bytes (The number of bytes required for an int). vali is the name given to the storage for those four bytes, which in line 5 is set to the value 5. This "place" has an address in computer memory - it is an actual address similar to a street address. The address of any variable can be obtained with the unary & operator.

&vali is the address of vali (vali, without the "&" operator is the data itself). In other words, when you access the data area using just vali (such as line 5 when vali is assigned it's value) you really do not care where the data is stored because that is not relevant- just the data is important. &vali, however, can tell you the address of the data.

Have a look at this next example:

01  #include <stdio.h>
02
03  main () {
04    float valf;
05    valf = 5.0;
05    printf("Value of valf   = %f\n", valf);
06    printf("Address of valf = %d\n", &valf);
07  }

On one computer, we got the following result;

Value of valf   = 5.000
Address of valf = 2147462964

So, when this computer ran the code, the CPU decided to put the memory location valf, at the what is akin to the "street" address: 2147462964 Memory Lane. Note: All addresses are represented as integers.

Just as one can take the address of a memory location, one can point to a memory location with the * operator. To accomplish this, one must first define a pointer as a particular data type. The definition

int* pvali;

will define pvali to be a pointer to the address of an integer: NOT a variable which holds an integer, but a pointer to it. The value of that address will be an integer in the mathematical sense (addresses are numbers, after all).

In essense, the C statement

type* variable;

(Where type can be a float, an integer, a double or some other data type, followed by the * symbol) is an address; the variable contains the address of that data. In the code below, we declare pvali to be a pointer to an integer. Then, in the same code, we go the next step: we formally point it to a specific address.

01  #include <stdio.h>
02  main(){
03    int   vali, valitemp; /* Define types */
04    int   *pvali;         /* pvali is a pointer to int */
05    vali = 5;  
06    pvali = &vali;        /* See below */
07    valitemp = *pvali;    /* See below */
08    printf(" valitemp = %d; valitemp);
09  }

  • Line 3 tells the CPU to carve out four bytes, and that vali will represent all of those 4 bytes, and carve out another 4 bytes for valitemp. Furthermore, by virtue of having declared vali to be an int, the CPU will know to interpret those four bytes as an integer.

    NOTE: You should now be aware of several data types: char, char*, int, int*, float, float*, double, double*, and FILE* (there is no FILE type, only a pointer to a file).

  • pvali - in line 4 -- is the new data type. It is not an int, but rather a pointer to an int. All memory on a computer has an address. More specifically, each and every byte has an address. The statement

    int* pvali;

    tells the CPU that pvali will represent a pointer to the address of a group of bytes. In other words, pvali will point to the address of an integer; once again, pvali is not an integer, but instead a pointer to the address of an integer. At the moment of definition - line 4 - pvali is not pointing at any specific address, and by default, is pointing to address 0: the NULL address. It is your obligation to point it at something.

  • Line 6 uses the & operator. The & operator - which you have seen before with the scanf() system call - finds the address of something. Thus, &vali does not access the 32 bits that comprise vali, but rather finds out the address in computer memory where vali is stored (the address of the first of those four bytes). Furthermore, since pvali is a pointer to a data type, it can hold an address. This means that pvali and &vali are the same thing. One more time - in line #6, the right hand side gets the address of vali. The left hand side, which is a pointer (and is designed to hold not an integer, but the address) is assigned the address of vali.

  • Now, take a look at line 7 and line 4. Both contain the * operator. The * in line 4 is used to tell the CPU that pvali will be a pointer to an int. It acts like an adjective in line 4. This is important, because the * operator performs differently in this declaraion then it does in the assignment state in line 7.

    In line 7, the * operator is actually performing an action; it acts like a verb when it's used in an assignment statement. So in line 7, it is going out and actually getting the data.

    When we use * in a variable declaration like line 4, it says "We're declaring a variable that points to an int". But if we're in an assignment statement like line 7, the * operator goes to the address pointed to by pvali and gets it's contents. This difference is often difficult to understand, spend some time making sure you understand:
    • the * in a declaration means "This variable will be a pointer",
    • and * in front of a pointer in an assignment statement statement means "go get the contents in the address pointed to by the variable".

  • It is common to call something which represents data to be an object. Thus, valitemp, in the code above, is an object consisting of 4 bytes.
    • & obtains the address of the object.
    • * retrieves the object to which a pointer points.
      • We call this: DEREFERENCING.
  • Just as * can access the data and take it out it is just as common to use * to access the address and put something there (see line 12 in the example below):

    01  #include <stdio.h>
    02
    03  main () {
    04    /* define vali to be an integer OBJECT */
    05    int vali;
    06    /* define pvali to be a pointer to an integer OBJECT */
    07    int *pvali;
    08
    09    /* place a 5 in the object called vali */
    10    vali = 5;
    11    /* print the value in vali */
    12    printf("vali contains %d\n",vali);
    13
    14    /* point pvali at the object known as vali */
    15    /* by equating it to the address of vali. */
    16    pvali = &vali;
    17    /* dereference the address to which pvali */
    18    /* is pointing, and put a 10 there */
    19    *pvali = l0;
    20
    21    /* print the value in vali */
    22    printf("vali NOW contains %d\n", vali);
    23  }

The above code will print the following:

vali contains 5
vali NOW contains 10

  • Nowhere in this code did we explicitly put the value 10 into vali.
  • On line 16, we retrieved the object to which pvali is pointing, and we put a 10 inside that object. But, do not forget that that pointer - pvali - was pointing at the address of vali, so, by default, the value of vali is 10.

As this point, you may be asking why all the fuss? Why bother to put something into a variable using a pointer when a simple assignment will suffice? The reason will become clearer in the next module; for now, think of using pointers as a different way of assigning values to memory locations.

Our next example is large enough to merit a separate window; take a look at Example5 and we'll step through the code here:

  • Line 8 & 12 declare our pointers, using the * operator. pvalf is a pointer to a float, and pvali is a pointer to an int.
  • Line 15 & 18 are important to understand. We're once again using the unary & operator, and it means "get the address for this variable".
    • In line 15, it retrieves the address of the memory location where the integer variable vari will be, and assigns pvali to that address.
    • Same for line 18, except it's for the floating point variable varf.
    • This means that when we make a change to vari, pvari will point to that changed value, and when we make a change to varf, pvarf will point to that changed value.
  • Line 21 assigns the value 11 to vari. Remember, since pvari was set to the address of vari, it now points to 11 also.
  • Line 24 assigns it's value in a different way; by dereferencing it, as we discussed in the previous example. As we mentioned, the unary * operator, in an assignment statement, is like the opposite of the & operator; it means "Go to the actual memory location pointed to by this pointer".
    • So in line 24, we're saying "Find the memory location pointed to by pvarf, and put the value 11.11 into it".
    • Keep in mind that since pvarf was assigned the pointer to the memory location of varf, that varf now contains 11.11 as well. These 2 variables are linked until the actual pointer pvarf is changed.
  • Lines 30 and 33 print the values of these variables to confirm that the pointer and it's corresponding memory location contain the same value.
  • We make the same kinds of assignments in Lines 37 and 40, except in this case, we assign the integer variables via dereferencing in line 37.
    • Remember from above; line 37 and it's use of the unary * operator is like saying "Find the memory location that this pointer points to, and put 22 into it". Since it points to the memory location of vari, we've effectively changed that variable's value with this dereference.
    • Line 40 is a regular assignment of the value 22.22 to varf; however, since pvarf points to this memory location, it now points to this changed value as well.
  • Lines 42 and 43 print out the values for these changed variables.

Our output for Example5 is:

L.1 integer = 11, real = 11.110000
L.2 integer = 11, real = 11.110000
L.3 integer = 22, real = 22.220000
L.4 integer = 22, real = 22.220000

The Most Common Error With Pointers
We will soon discuss the most common error in C programming. This error is so profound and so common that we will devote a special section to it. The following code will crash. While reading it, please recall that a short is also an integer except that it is held in only 2 bytes as opposed to 4.

Example 6

01  main() {       
02    short vali;  
03    short *pvali;
04
05    *pvali = 10; 
06  }

Line 3 will declare that pvali is a pointer to a short. However, it has not yet been pointed to anything. Thus, the address is, by default, NULL (or zero).

So, if one tried to de-reference that space, the code will crash because it atempts to access address 0. On most systems, address 0 is the address of the operating system (in older DOS-based PC's, doing this would wipe out the operating system and require a re-installation; on newer sytsems, and on UNIX systems, address 0 is protected, and only the program dies, but not the operating system).

This is the error that you will see if you run this code:

Segmentation fault (core dumped)

You will see it again, and again until you learn to NEVER de-reference a pointer that has not yet been set so it's pointing to something. To do this properly, one must point pvali at a space of memory. Then you can do whatever you wish with that pointer. Let's update Example 6 in the followiing way:

EXAMPLE 6-updated

01  main() {
02    short vali;
03    short *pvali;
04    pvali = &vali;
05    *pvali = 10;
06  }

Notice the only difference is the addition of  the new line 4, pvali = &vali.

When this line is executed, pvali will be pointing at something (Namely, the memory location of the short vali). Once pvali is pointing at something, it can be dereferenced.

That's alot of information about pointers. In the next module, we'll learn about allocating memory to pointers dynamically, during the program run, using the malloc() statement. But for now, just spend some time trying to digest all the information on pointers that we've gone over.

Continue to the Apply Section ->

Website by Joshua Bleier