Objects First

Advanced C Programming

Pointer Arithmetic

As noted earlier in the introduction to
pointers,
pointers are a difficult concept in C.
Some of this difficulty is created by C programmers
who make extensive use of C's pointer arithmetic capabilities.

This statement makes use of the fact that the pointers,
cp and src, are simply memory addresses
and may be incremented using the ++ operator
just as we apply it to an integer.

This coding may be concise, but it relies on the operator precedence
rules, and thus violates a
rule suggested earlier
!
Since ++ has precedence over *, the abbreviated coding
is equivalent to:

*(cp++) = *(src++);

in which the increment is applied to pointers, not the objects to which
they point.
Thus this terse code has the desired effect:
fetch the character from the address in src (incrementing srcafter it has been used to fetch a character)
and store it in
the address in cp (incrementing cpafter it has been used to store a character).
The two pointers move through the source and destination blocks of memory -
alternately fetching (storing) a character and incrementing the address to
point to the next location.

General pointer arithmetic

Since we can apply the ++ operator to a pointer, we can apply any
other arithmetic operation also:

Array addressing and pointer arithmetic

When we access an element of an array, we have two alternative
and equivalent ways to address the element.
For some char array,

char a[N];

the (i+1)th element may be accessed with:

c = a[i];

or

c = *(a+i);

In the second case, we use the fact that the name of an array
is a pointer to its first element and use pointer arithmetic to
advance the pointer to the address i bytes past the
"base" address of the array.

Since both methods for accessing array elements are equivalent,
they may be used interchangeably.
This applies no matter how the array is defined or created.
In particular, dynamically allocated arrays can be addressed
with the indexed form if it's convenient and helps to make
your code easier to understand.

Note that because we allowed
the pointer buffer to "move"
through the buffer, we were careful to keep an unmodified pointer,
start, to the beginning of the buffer so that we could
use it again or free it.
Forgetting to keep this pointer to the
start of an array is is a common trap for new C programmers!
They find that a buffer apparently contains garbage -
because they're looking at memory beyond the original
buffer.

Pointers to general types and structures

Arithmetic on pointers to objects which are not characters
obeys a special rule.
If we create an array of structures:

The increment operation applied to any pointer
advances the address by the size of the object to
which the pointer points.
(This rule always applies whether the pointer points
to a structure or to an inbuilt class: in the character case, the
size of the object is 1 byte, so incrementing the address
is the desired operation.)
All other arithmetic operations on pointers are similarly
interpreted in units of the size of the object that the point
points to.
Thus, to access every third element of abc_array:

(or something even more abstruse).
This means that the compiler doesn't know how to increment
the addresses, cp and src, because it doesn't
know what they point to.
This can be fixed in a number of ways:
type-casting is commonly used:

In this example, the void * pointers are cast to
char * ones before being used -
enabling the compiler to work out that the address should be
incremented by one byte.

Good coding practice

It should be obvious to you that the possibilities for generating
"neat" and compact codings are almost endless.
Regrettably, a whole generation of C programmers has explored
many of the possibilities and has already produced a good fraction
of the total set of code variations that take advantage of pointer
arithmetic.
I say "regrettably" because many of these codings, although ingenious,
would certainly not qualify as easily intelligible:
it's possible to produce obfuscated expressions that can take hours to
interpret!

The *pointer++ pattern which steps through a C array
(of any type) is compact and found in so many existing C programs
that suggesting that it be replaced by pointer[i]
is, by now, unrealistic! Thus this is one pattern that any
one maintaining C programs must
understand.
Consequently, using it yourself is reasonable:
any competent C programmer will immediately understand what
the pattern does.

More imaginative uses of pointers should, however, be considered
with care!
Occasionally, these uses could be justified by the argument that they
enabled the compiler to produce much faster code.
Such arguments are rarely valid now:

modern optimising compilers are often good enough to reduce the
longer, more easily read expressions to code that's as
efficient (fast and compact) as the hand-optimised version
and

computers are generally so fast that cost-benefit analyses
will almost always come down on the side of the more easily
maintained code. (Even if it's slower than a hand-optimised version,
the extra computer time has a cost that's negligible compared to
the cost of the programmer's time to optimise code by hand!)

There are usually more explicit, more easily understood ways of
producing the same result, making the "elegant" or "ingenious"
uses of pointers pointlessly bad coding!