Pointers And Memory - Contents
A D V E R T I S E M E N T
When a pointer is first allocated, it does not have a pointee. The pointer is "uninitialized"
or simply "bad". A dereference operation on a bad pointer is a serious runtime error. If
you are lucky, the dereference operation will crash or halt immediately (Java behaves this
way). If you are unlucky, the bad pointer dereference will corrupt a random area of
memory, slightly altering the operation of the program so that it goes wrong some
indefinite time later. Each pointer must be assigned a pointee before it can support
dereference operations. Before that, the pointer is bad and must not be used. In our
memory drawings, the bad pointer value is shown with an XXX value...
Bad pointers are very common. In fact, every pointer starts out with a bad value.
Correct code overwrites the bad value with a correct reference to a pointee, and thereafter
the pointer works fine. There is nothing automatic that gives a pointer a valid pointee.
Quite the opposite — most languages make it easy to omit this important step. You just
have to program carefully. If your code is crashing, a bad pointer should be your first
suspicion.
Pointers in dynamic languages such as Perl, LISP, and Java work a little differently. The
run-time system sets each pointer to NULL when it is allocated and checks it each time it
is dereferenced. So code can still exhibit pointer bugs, but they will halt politely on the
offending line instead of crashing haphazardly like C. As a result, it is much easier to
locate and fix pointer bugs in dynamic languages. The run-time checks are also a reason
why such languages always run at least a little slower than a compiled language like C or
C++.
Two Levels
One way to think about pointer code is that operates at two levels — pointer level and
pointee level. The trick is that both levels need to be initialized and connected for things
to work. (1) the pointer must be allocated, (1) the pointee must be allocated, and (3) the
pointer must be assigned to point to the pointee. It's rare to forget step (1). But forget (2)
or (3), and the whole thing will blow up at the first dereference. Remember to account for
both levels — make a memory drawing during your design to make sure it's right.
Bad Pointer Example
Code with the most common sort of pointer bug will look like the above correct code, but
without the middle step where the pointers are assigned pointees. The bad code will
compile fine, but at run-time, each dereference with a bad pointer will corrupt memory in
some way. The program will crash sooner or later. It is up to the programmer to ensure
that each pointer is assigned a pointee before it is used. The following example shows a
simple example of the bad code and a drawing of how memory is likely to react...
void BadPointer() {
int* p; // allocate the pointer, but not the pointee
*p = 42; // this dereference is a serious runtime error
}
// What happens at runtime when the bad pointer is dereferenced...
Why Are Bad Pointer Bugs So Common?
Why is it so often the case that programmers will allocate a pointer, but forget to set it to
refer to a pointee? The rules for pointers don't seem that complex, yet every programmer
makes this error repeatedly. Why? The problem is that we are trained by the tools we use.
Simple variables don't require any extra setup. You can allocate a simple variable, such as
int, and use it immediately. All that int, char, struct fraction code you have
written has trained you, quite reasonably, that a variable may be used once it is declared.
Unfortunately, pointers look like simple variables but they require the extra initialization
before use. It's unfortunate, in a way, that pointers happen look like other variables, since
it makes it easy to forget that the rules for their use are very different. Oh well. Try to
remember to assign your pointers to refer to pointees. Don't be surprised when you forget.
Back to Table of Contents
A D V E R T I S E M E N T
|
Subscribe to SourceCodesWorld - Techies Talk |
|