Consider: When you have a pointer to an object, it is a name for exactly that object and no other. That sounds trivial, but look at the following two expressions:
np node[i]
The first points to a node, the second evaluates to (say) the same node.
But the second form is an expression; it is not so simple.
To interpret it, we must know what
node
is, what
i
is, and that
i
and
node
are related by the (probably unspecified) rules
of the surrounding program.
Nothing about the expression in isolation can show that
i
is a valid index of
node,
let alone the index of the element we want.
If
i
and
j
and
k
are all indices into the node array, it's very easy to slip up,
and the compiler cannot help.
It's particularly easy to make mistakes when passing things to subroutines:
a pointer is a single thing; an array and an index must be believed
to belong together in the receiving subroutine.
An expression that evaluates to an object is inherently more subtle and error-prone than the address of that object. Correct use of pointers can simplify code:
parent->link[i].type
vs.
lp->type.
If we want the next element's type, it's
parent->link[++i].type
or
(++lp)->type.
i
advances but the rest of the expression must stay constant;
with pointers, there's only one thing to advance.
Typographic considerations enter here, too. Stepping through structures using pointers can be much easier to read than with expressions: less ink is needed and less effort is expended by the compiler and computer. A related issue is that the type of the pointer affects how it can be used correctly, which allows some helpful compile-time error checking that array indices cannot share. Also, if the objects are structures, their tag fields are reminders of their type, so
np->left
is sufficiently evocative; if an array is being indexed the array will have some well-chosen name and the expression will end up longer:
node[i].left.
Again, the extra characters become more irritating as the examples become larger.
As a rule, if you find code containing many similar, complex expressions that evaluate to elements of a data structure, judicious use of pointers can clear things up. Consider what
if(goleft) p->left=p->right->left; else p->right=p->left->right;
would look like using a compound expression for
p.
Sometimes it's worth a temporary variable (here
p)
or a macro to distill the calculation.
Contents
Variable names
Procedure names