Using C Pointers
Copyright 2001 Berry Embedded Software, Inc.
If you want to work in the embedded world, you’d better know C inside out. C inside out is |.
One of the most misused, misunderstood aspects of C is the pointer. A pointer is nothing more than a memory location in which you store the address to something else! A pointer can even store the address to another pointer. Its versatility gets so many into big trouble. In the industry, we refer to this trouble as “pointer doo doo”.
You declare a pointer with the syntax
int* a;
or alternately
int *a;
Seems simple enough, doesn’t it? Not quite… depending on where you declare the pointer, it may or may not be initialized to any consistent value! If it is in global space, its contents probably get initialized to zero. But, if you declare it internal to a routine, it will be placed on the stack or perhaps in a processor register. Whatever happened to be in that stack memory location or register before the declaration now becomes an address!
Think about the consequences of forgetting to set the contents of the pointer before you use it. There is about a –33.1% chance that the new “address” points to any place that won’t do bad things to the system running the code. If you’re lucky, you might change a data value and get the wrong answer from a routine you know has been thoroughly tested. The customer must be crazy to report faulty operation of that routine. It still works when you retest it… so you ignore the customer and he takes his business elsewhere.
If you aren’t lucky, and most of us aren’t, it will overwrite the register that controls the megawatt laser in a weapons pod. The customer will scream and yell and jump up and down on your toes (then politely bill you for the damage) because the system inexplicably turned on the laser inside its canister, burning out a half million dollars worth of mirrors and diodes. You can try to argue that that can’t happen, but the smoking debris is hard to dispute.
What I’m talking about is something like this: you run a routine that stores a number on the stack that happens to match the address of the laser control register, then your feature-laden routine runs. It just so happens the stack lines up to give you the same contents the prior routine did. Note, they don’t have to be any where near each other physically or chronologically. All that has to happen is for the stack pointer to come up at the same spot in your routine.
#define TURNITON 0x47c
#define TURNITOFF 0x317
#define LASERCONTROL 0x4357e95
int OtherRoutine(int Avar)
{
int AStackVar;
int a;
int b;
.
.
.
a = 0x6a97;
b = 0xa1b3;
AstackVar = a * b;
.
.
}
some time later:
int MyPointerProgram(int SomeVariable)
{
int *MyPointer;
int a;
int b;
.
.
a = 0x52;
b = 0x0e;
*MyPointer = a * b;
.
.
}
You just gave the laser the command to turn on when the stack happens to line up so that MyPointer is assigned the same memory location AStackVar used. The syntax *MyPointer = a * b; means the contents of MyPointer equals a times b. Poof goes your career.
Don’t scoff, I see these errors all the time! It is probably the number one error they have to pay me big bux (otherwise known as consultant rate) to find and fix. The error may, as in this example, have absolutely nothing to do with a register or any kind of number that looks like the address involved or the turn on code involved. The error is probably one line in 100,000+ lines of C code. These things can and do lie dormant for many years. An engineer was telling me last week that they had fought a bug in a controller for 4 years and never did track it down until they ported the software to a new processor and the compiler flagged an uninitialized pointer. The old compiler didn’t produce any such warning, and you can’t count on even a good compiler catching your goofs.
We’ll get into casting and pointers to pointers next time. Until then, initialize those pointers by declaring them like this:
int *Apointer = NULL;
That way, the worst that will happen is you get a null pointer run time error that then becomes a lot easier to find.