Pointers
User Rating: / 1
PoorBest 
Written by Bryce Ringwood   

The next example (the morse code beacon) uses 'C's pointers. I don't intend to include an entire C/C++ tutorial here, because there are web-sites that do that really well, and books that do it better still. Bearing in mind that this series was done to assist a friend, this is how I might have explained pointers.

Pointers are simply numbers that point to a memory address where entities are stored. Most people are familiar with a street address - say number "42 Beach Road" which describes how to get to a particular house containing occupants (maybe). Computer memory is like a town that has a single long street with a huge number of houses.

The houses in computer memory are unique and store "things" (or entities) such as int, float, bool, double etc. data types. If you are an int or a char, you only need a small house containing one memory location. If you are a double, you might need 8 locations. In C++, the memory locations can also be used to store objects. Other things occupy memory too - such as functions, structures (which we won't be talking about too much) and so on.

A very important entity not mentioned so far is an array. An array is simply a list of objects or data types. I think nearly all computer languages have arrays, but C and C++ are special in the way they make use of pointers to address variables contained in an array. You might be surprised that later languages like Java do not have pointers, and of course, earlier languages, such as BASIC and FORTRAN did not have them.

Indeed, one enterprising(?) FORTRAN programmer writing a large engineering program decided that to conserve memory, he would reserve a huge block as an integer array in a COMMON block (accessible to all program units) and then he proceeded to allocate positions in memory using arrays and bits of arrays for all his variables, data etc. It was horrible.

The C/C++ langauges do the job properly and  pointers are very useful things. They are also quite easy to use, provided you can keep a clear head. If you are finding them difficult - don't worry, just use arrays and get around to them in your own good time, after all, this is for a hobby, not for work. It is said that you don't have to use pointers in C/C++ at all, , but I have got far too familiar with them and use them all the time. I'm going to see if I can rewrite the Morse Code program using just arrays.

Before you begin

... have a C/C++ compiler available. A compiler on an old MSDOS computer is best, because its going to be faster for small test programs. The free Microsoft Visual Studio express takes a bit long to get started. There's also absolutely nothing wrong with gcc (Linux) or cc (UNIX). I'll stick with turbo 'C' for as long as I can and use Visual Studio Express for the C++ examples. Let's also not forget good old Watcom C/C++.

Pointers have Data Types

The reason for this is that the different data types all have their own storage requirements. On a 16-bit computer, an int  may have one word of storage. A float might need 4 words and a double  8 words. Of course, a character string needs as many bytes as there are characters in the string  + 1 for the null terminating character. Apart from these obvious data types, classes, functions and structs (which I won't be covering) can all have pointers - since they live in memory.

The reference operator '&'

Let's begin with the operator '&' - the reference operator. This will provide the memory address (location) where a variable is stored:

#include <stdio.h>

int main(int argc, char** argv)
{
       int a=3;   
       printf("%x",&a);
       return 0;   
}

The "%x" in the above example tells printf to print the result in hexadecimal format. If you compile and run the above example, you will get a result such as "be2" - a memory address.

Note that in C++, there is a slightly different shade of meaning to the & operator. The following program

#include <stdio.h>
void func(int
&a); // Function Prototype
void func(int
&a)
{
    a=a+4;
}

int main(int argc, char argv[])
{
    int j= 3;
    printf("j is %d\n",j);
    func(j);
    printf("j is now %d",j);
    return 0;
}

The code is in blue because it is C++ code, not 'C' code.
This will compile in most C++ compilers (albeit with warnings about not using argc and argv), but will probably NOT compile in a C compiler. What is happening here is that we are passing argument j by reference and NOT by value, as is the normal practice for C/C++ functions. Sometimes its very useful, other times it leads to trouble - so its best not to do this all the time. (I am not sure whether 'Wiring' uses the C or C++ definition of the operator - take care).

The dereference operator '*'

If you understand the & operator - the * operator does the exact opposite. Lets say you have an address 3b7 for example - the * operator will return the value of whatever is in that address.

if int a = 43;

and b is an int, then what will be the value of b, if b=*&a ?

Clearly, &a is an address, and * will resolve the contents of that address, so b ==  a. (My short hand for b is equal to a).Now, let's look at the 'C'  version of the program that passes arguments by address, rather than by value as is the norm:

#include <stdio.h>
void func(int* a); // Function Prototype
void func(int* a)
{
    *a=*a+4;
}

int main(int argc, char argv[])
{
    int j= 3;
    printf("j is %d\n",j);
    func(&j);
    printf("j is now %d",j);
    return 0;
}

Compile and run the program, and you will see it does exactly the same as the C++ program presented earlier. (It would also compile under C++).Pointers

The function prototype is looking for a value "*a" , as is the norm for a C/C++ program. a, is of course, an address. The function obligingly gets the value at address a (*a, in other words) adds 4 to it, to produce 7, and stores 7 at the address of a. The value a does not change, but on return, the contents have, and the program duly prints 7. When we call the function, we must call it with an address, because we declared the function parameter to be of type int*. We therefore pass &j, not j to the function. You should try this for yourself.

I confess to a little skulduggery. You probably saw that I declared the function parameter as:

int* a, not int *a. What's the difference ? As far as the compiler is concerned - none. Writing it as I did makes it more clear that we are passing a (an address) as the parameter. (I think.)

This gets us back to the very first program - the resistor program, where we used the & operator to retrieve values from input.

Arrays

There is a relationship between arrays and pointers in 'C' that is incredibly strong. Why? - because they are almost the same thing, as we shall see.

As an example from mathematics, a vector is an array of numbers that can be used to describe the position of something relative to something else - often an origin. Suppose we move 3.6 units in the x-direction, 5.2 units in the y-direction and 4.6 units verically, then the position vector would be

a = (3.6 , 5.2 , 4.6)

In C/C++, we would express this as follows:

double a[3] ={3.6,5.2,4.6};

Another example would be a character string:

char s[17] = "This is a string"; or

char s[17] = {'T','h','i','s',' ','i','s',' ','a',' ','s','t','r','i','n','g','\0'}; Ugh. Notice the terminating NULL character.

Arrays are based  from zero in C/C++, so s[0] == 'T'; s[4]==' '; and so on.

In our vector example, what would be the value of a[2] ? and the value of a[3] ? (OK - Gotcha!).

Of course arrays can be of any type so in C++, its OK to have arrays of objects.

By the way, you can let the compiler count for you:

char s[] = {'T','h','i','s',' ','i','s',' ','a',' ','s','t','r','i','n','g','\0'};

Is also good to go .

char* s = "This is a string"; works, but

char* s = {'T','h','i','s',' ','i','s',' ','a',' ','s','t','r','i','n','g','\0'};

does not.Character Arrays

If we have arrays - do we need pointers ?

Consider the following code (from Microsoft Visual Studio):

 

 

 

 

 

// ex11.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

void ex(double a[], double b[]);

void ex(double a[], double b[])
{
     double temp;
     temp = a[0];
     a[0]=b[0];
     b[0]=temp;
}
int main(int argc, char* argv[])
{
    double p[1] ;
    double q[1] ;
    p[0]=4.5;
    q[0]=6.2;
    ex(p,q);
    printf(" p[0] = %f  q[0] = %f",p[0],q[0]);
    return 0;
}

I don't like it at all. But as far as I can see, if you program in Java (A C++ like language), then you would have to do something similar.

Try writing the above program using C++ references, then in 'C' using pointers.

Now you know how to index an array - but if

char* s = "This is a string";

How do we idex the third character in the string  using pointer notation? Well, since s is the address of the "Zeroth" character, 'T', the third character will be at address *(s+2) because the characters in the string all occupy consecutive memory locations. Note that *s + 2 means something else entirely. (it =='V' - do you see why ?);

Arrays of arrays

If an array of numbers is a vector, an array of an array of numbers is a Matrix. Matrices and vectors are used for graphics transformations, systems of linear equations and so on. I'm not going to dwell on this, since we are not using the PIC and Arduino for numerical analysis. (Unless you write and tell me that you simply must know about these applications.)

Most often, we use arrays for string manipulation, as in the example that follows:-

// ex12.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"


int main(int argc, char* argv[])
{
 for(int i=1; i < argc; i++)
 {
  printf("Argument %d is %s\n" ,i, argv[i]);
 }
 return 0;
}

This program reads the parameters from the command line, and outputs them as a result:

C:\Projects\ex12\Debug>ex12 hello world
Argument 1 is hello
Argument 2 is world

You can declare an array of arrays as:

char* s[] = { Arrays of arrays

            "triode",

             "tetrode",

             "pentode"

}

or you can use the

char** s; form

Now, let's try to use the char** s; form- and see what goes wrong.

The program that follows is typical of what happens when you're not thinking - and typical of what gives pointers their bad reputation:

 

 

 

 

 

 

 

int main(int argc, char* argv[])

    char **s;
    *s="triode";
    *(s+1)="tetrode";
    *(s+2)="pentode";

 

    for(int i=0; i < 3; i++)
    {
        printf("Value %d is %s\n" ,i, s[i]);
    }
    return 0;
}

If you compile this - it WILL compile! As soon as you run it, it will crash - please try. The problem is that we have not made room for the little array of pointers that point to each character string containing the " toob type".

There are two 'cures' - the first is to use the

char* s[] = {"triode",tetrode","pentode"}; definition protocol, the second is to explicitly define that little array pointer and allocate memory for it. Unfortunately, you cant say:

char**  = {"triode",tetrode","pentode"};

- it just doesn't work. 

Dynamic Memory Allocation

In C++, there is an operator new, which can be invoked to allocate memory from "the free store" - whatever memory is left over after you put your program into memory. Or maybe the PICS data memory or whatever - just some free memory. When you have finished with the memory, you should free it up for later with the delete operator.

Using new on a character array would look like this:

char *s = new char[256];    // or whatever example is for 256 chars

to be followed by

delete [] s;

Here's our example revisited:

int main(int argc, char* argv[])

    char**s  =
new char*[3];
 
    *s="triode";
    *(s+1)="tetrode";
    *(s+2)="pentode";

 

    for(int i=0; i < 3; i++)
    {
        printf("Value %d is %s\n" ,i, s[i]);
    }

    delete [] *s;
    return 0;
}

Sadly - IT DOES NOT WORK ON THE ARDUINO - "Wiring" does not have an operator new. Now what ?

We use 'C's malloc:

int main(int argc, char* argv[])

    char**s ;
    if((s = (char**)malloc(3 * sizeof(char*)))==NULL)
   return0;
    *s="triode";
    *(s+1)="tetrode";
    *(s+2)="pentode";

 

    for(int i=0; i < 3; i++)
    {
       printf("Value %d is %s\n" ,i, s[i]);
    }
    free(s);
    return 0;
}

However you allocate memory - be careful that you free up exactly what was allocated. Not doing so will introduce a memory leak. Memory leaks can be insidious - particularly in microcontrollers with long-running programs, as day by day a byte or so doesn't get free'd. The program runs for  2 years and then - disaster as a whole airport comes to a standstill.

Look at my own CalcPad program - I know where the problem lies, but I can't fix it very easily because the leak is in  programming interface functions that I can't change.

Arguments against using pointers

Some knowledgable people maintain that pointers are "bad', because they are a source of coding errors. typical problems being: not allocating memory (see above) and not freeing memory. Other problems being writing beyond memory/array boundaries.

This being the case, languages like Java were developed. I have to say, I code just as badly in Java as I do in C/C++ and make just as many mistakes.

In my FORTRAN days, I had a programmer who brought down a huge mainframe system - she'd gone out of bounds on an array.

SO - Here's the challenge: Rewrite the Morse Code Program without using pointers. You are allowed to use array notation. First correct answer gets an honourable mention in the annals. (That's annals not anals.)

Conclusion

This article is intended to supplement other references on programming pointers. In particular, I haave concentrated on character strings. When programming in C++ - look at the string  class also.

Most of the programs here have been written using either the Watcom C++ compiler or Microsoft Visual Studio Express 2010. Hopefully, the article presented here will enable you to follow the Morse Code program, which follows.

 

 

 

 

 

 

 
Joomla template by a4joomla