Techno-Plaza
Site Navigation [ News | Our Software | Calculators | Programming | Assembly | Downloads | Links | Cool Graphs | Feedback ]
 Main
   Site News

   Our Software

   Legal Information

   Credits

 Calculators
   Information

   C Programming

   Assembly

   Downloads

 Miscellaneous
   Links

   Cool Graphs

   Feedback Form

C Programming Lessons

TIGCC Programming Lessons

The following is part of a series of lessons designed to help teach people to program in C for the TI-89, 92+, and V200 calculators using the TIGCC development environment.

If you wish, you can download the program source code, project files, and binaries here.

Lesson 5: Pointers and Arrays

Step 1 - An Introduction to Pointers

Pointers are core to the C programming language. They allow us to access memory directly, which lets us write very efficient and thus very fast programs. Most modern programming languages have some form of pointers, and pointers are probably one of the most difficult concepts to master in C.

So, what is a pointer? Basically, it's an address in memory somewhere. We don't know where, and we don't need to know where. That's why we have the pointer. What do we use pointers for? Everything! No, I'm not kidding. Pointers are one of the most useful concepts in C. The reason is that pointers allow us to keep a single copy of a variable (i.e. the memory we are using) and change that memory from anywhere in the program. This may sound like a bad idea, and in practice, it's a difficult thing to control, but once mastered, you will realize why C is the default standard language for building most any real program today.

We have already seen pointers, and used pointers, but we haven't really talked about what a pointer is and why it is special. This is what this lesson is for.

Step 2 - Arrays and their Connection to Pointers in C

This lesson does not cover only pointers, but talks about arrays too, so we should probably discuss the special relationship between pointers and arrays.

A pointer is a tool used for keeping track of a certain spot in memory. In C, arrays are simply groups of data stored sequentially in memory. Because they are stored sequentially, we can keep track of the first item in an array with a pointer to that item, and use that as an index point for every other item in the array.

Step 3 - Using Arrays in C

Now that we know what arrays are in relation to pointers, let us talk about how we will use them in C. C makes array handling very simple by abstracting all the hard parts we don't want to deal with. Basically, there are two things we need to handle when dealing with arrays: how to create and address them, and how to manipulate the array (that is, move data into or out of the array).

Step 3a - Creating Arrays

Let's look at a simple array definition.

int array[20];

This is one of the ways we create an array. We declare the type of the array (what kinds of values the array will hold -- this tells C how to access the array internally so we don't have to deal with that stuff), and the size of the array (we have to know how big the array will be before we can use it so the computer can reserve space for it). So, to recap, an array is declared by using it's type, the name we want to call the array, and the size of the array specified inside brackets [].

Suppose we wanted a character array instead.

char array[20];

All we had to do was change int to char. Simple, isn't it?

Step 3b - Manipulating Arrays

Well, creating an array is pretty useless if we don't have a way to manipulate the data inside the array. So let's talk about how to manipulate the data inside the array. We will start with how to put data into an array.

array[index] = value;

Again, pretty simple. We just put the name of the array, then the position in the array we want to move data into, and use the equality operator. It's fairly intuitive, when you see the syntax.

The index can be a number, or a variable with a number stored in it. So, both of the following are legal:

int index = 2;
  
array[0] = 20;
array[index] = 13;

Most of the time, it will be easier to use a variable to keep track of the index, but there are times when you may want to access the array directly.

Well, now that we understand the basic syntax for storing data in an array, what about taking data out of an array? Well, it's pretty much the same syntax, only we reverse the source and destination. (Remember, the stuff on the left is always the 'variable')

So, the basic syntax for grabbing data from an array would be:

variable = array[index];

Again, the index can be a number or a variable. The variable must be a variable, obviously.

Step 3c - Working with Arrays

Well, you know the basic syntax for using arrays, so how are they used in practice? Mainly in loops. We have a variable, which I always call 'loop', then we use a for loop and use the loop variable as the index. Then we do something to the array, as needed. I'll put a small pseudo-example, but I think you can get the idea even without it:

int loop;

for (loop = 0; loop < arraysize; loop++) {
	array[loop] = something; 	// or
	somevar = array[loop];		// or
	somefunction(array[loop]);	// only one of these three things is generally used
}

As you can see, arrays are very easy to use in C, and they're also a good topic to cover when we're talking about pointers, as it helps me explain other concepts.

Step 4 - An Introduction to Pointers

Okay, now that we covered arrays, let's talk about pointers in general. A pointer, in general, is a number representing a memory address. We call it a pointer because it is pointing to a place in memory we want to work with.

We have used pointers before, but have not gone into detail about them. So, let's start by how to declare them.

int *ptr;	// declares an integer pointer
void *ptr;	// declares a pointer of unknown type (useful, but easy to misuse)
char *ptr;	// character pointer, often used for string literals

Basically, a pointer is declared by putting a asterisk * in front of the variable name. In addition to regular variable types, you can also have void pointers, which are just pointers of which the type is unknown. Eventually, you have to treat a void pointer as a certain kind of data, because there is no standard way to handle it, but that's a little advanced for this introduction, so I will avoid void pointers until we have a real use for them. You saw a void pointer in the lesson 2 in the OSdequeue example.

We can use any type of variable as a pointer. You might wonder why we would need to know the memory address of a variable. Two important reasons come up. First, when dealing with arrays and other large blocks of data, it would be inefficient to pass all that data around rather than just tell you where it is. Second, if we use a pointer, we can change the specific variable without needing to copy it's value and then assign a new value, such as a return value.

Step 4a - Pointer Basics

Well, we know what pointers are, and have a general idea of why they are used, so now that we have this information, how do we put it into practice? Easy! Let's try this for starters:

Start TIGCC and begin a new project. Create a new C Source File called pointers.


#include <tigcclib.h>

void change_vars(int *x, int *y, int *z) {
    *x = 256;
    *y = 128;
    *z = 64;
}

// Main Function
void _main(void)
{
    // create three variables
    int x = 50, y = 75, z = 150;

    // clear the screen
    clrscr();

    // print the "before" values
    printf("Before:\n     x: %d\n     y: %d\n     z: %d\n\n", x, y, z);

    // change the variables and print them again
    change_vars(&x,&y,&z);
    printf(" After:\n     x: %d\n     y: %d\n     z: %d\n", x, y, z);

    // wait for user to press a key before exiting
    ngetchx();
}

Step 4b - Compile and Run the Program

Save the file and compile the project. Send it to TiEmu and run the program. It will look like this:

TI-89 AMS 2.05 pointers.89z TI-92+ AMS 2.05 pointers.9xz

Step 4c - Program Analysis

As we have done in every lesson up to this point, we will analyze our program. This program is fairly simple, but is also an idyllic example. Most pointers you use won't be so clear and easy. But let's take it one step at a time.

// create three variables
int x = 50, y = 75, z = 150;
	
// clear the screen
clrscr();

You have seen these things before, but don't forget the clrscr() is used when we need to use printf(), and ClrScr() is used when we don't use printf(). Since we are about to use printf(), we need to use clrscr() to clear the screen as opposed to ClrScr(). This distinction becomes important when you do many printf()'s.

// print the "before" values
printf("Before:\n     x: %d\n     y: %d\n     z: %d\n\n", x, y, z);

Now we are going to print two sets of values. The values of the variables before we change them using pointers, and the values after we change them using pointers. We have used printf() before, but I will reiterate it's use in case you have forgotten. The string has several escape sequences, the \n's, which are new line markers. Every time we encounter a new line marker, printf will move to the next line. The other kind of special character sequence is the format specifier. The one used here '%d' tells the printf() function to replace this with an integer value, which we will give it later. The integer values we use here are the x, y, and z integer variables. So, this function prints our string with the values of the x, y, and z variables and some new lines. Printf() is a very powerful function, but it is also slower because of its versatility.

// change the variables and print them again
change_vars(&x,&y,&z);

Our next program part is a function call. We are going to call the function change_vars, and we are passing it three parameters, the x, y, and z variables. But to send them as pointers, (remember we declared them above as regular variables), we need to use a special operator, the address of operator &. The & operator will return the address of a variable so we can use it as a pointer. Remember that pointers are just memory addresses.

You may have noticed that we didn't use a function prototype. Function prototypes are only strictly needed if you are going to use functions before the compiler has seen them. If the compiler has already seen the function, it can get the signature itself. Since change_vars is defined before the _main function, and is only used inside _main, there is no strict need for a prototype. You might want to try moving the function to after the _main function and see what happens. Then try adding a prototype at the top and see how it fixes the problem.

Now, we must also specify to the change_vars function that the values we are sending it are pointers. We can do this by using the method above, putting the * asterisk in front of the arguments of the change_vars function. The asterisk is called the dereferencing operator, as you will understand in a minute. So, let's take a look at the change_vars function:

void change_vars(int *x, int *y, int *z) {
	*x = 256;
	*y = 128;
	*z = 64;
}

We see the function has a void return type, which means the function doesn't return anything. This is a good thing, because we are changing three variables, and we know no way to return 3 values from a function.

As you can see in the function argument list, all the variables have an asterisk * preceding them. This tells the function that these variables are pointers, so do not treat them like regular variables.

You will notice one final quirk about what we do once we get into our function. We have to use the dereferencing operator again. Why? Well, we just told the compiler to treat the variable like a pointer. So if we assign it a value now, we won't be changing the value of the memory pointed at by the pointer, we will be changing the pointer to point to a new address. That would pretty much ruin our day. Not in this simple example, but if you tried to use the pointer after changing the address, the program would almost certainly crash.

So, what does the dereferencing operator do here? Simple. We want to assign a value to the memory pointed at by the pointer. To do that, we must 'dereference' (reference being another word for point to, dereference must mean to find what is being pointed to) the pointer to get the place in memory to change. So, instead of changing our pointer, we have changed the value of the memory the pointer pointed at, and C did all the work for us. Wasn't that simple?

You might like to see just how much arrays and pointers are alike. Try changing the *x to x[0]. You'll notice it does exactly the same thing. Because array variables are just pointers to the first item in the array, a pointer is effectively an array as well. In this case, we have three arrays of size 1. Pointers are arrays and arrays are pointers. A little glib, but basically true.

printf(" After:\n     x: %d\n     y: %d\n     z: %d\n", x, y, z);
	
// wait for user to press a key before exiting
ngetchx();

Well, the prinf() function hasn't changed much. I made the printing line up with the first printing, so it looks nice, but it's the same basic thing. And we know that ngetchx() will wait for the user to press a key, so they can see the output before the program exits.

See, pointers aren't so hard right? Well, they get harder, but that covers the basics. You are probably asking yourself why you would use pointers for such a trivial program. The short answer is you wouldn't, but they became big time savers in larger programs, or in programs that can't return a value (like a string -- you can't return a string, but you can return a pointer to a character array). This is where pointers are most important, well, that and for efficiency. So why not take a look at a slightly harder program using pointers.

Continue this Lesson in Part II

 

Copyright © 1998-2007 Techno-Plaza
All Rights Reserved Unless Otherwise Noted

Get Firefox!    Valid HTML 4.01!    Made with jEdit