Quote:
>char *foo;
>And foo is a pointer to the first element of an array, I suppose that :
>char **foo;
>Foo must be a pointer to the first element of an array which is an array by
>itself. ...
This is very confusing.
Try drawing pictures. Get yourself a piece of paper and draw some
boxes and arrows on it:
+---------+ +-----+-----+-----+-----+
| p1 -----> | 'a' | 'b' | 'c' | '\0'|
+---------+ +-----+-----+-----+-----+
^
|
+---------+
| p2 |
+---------+
Here p1 and p2 are "char *" pointers. p1 points to the first of
four "char"s, while p2 points to just one "char" (which happens
also to be the last of those four "char"s that p1 points to, but
so what?). (I made the pointer boxes "bigger" than the char
boxes since sizeof(char *) is usually greater than 1. Here it
does not really matter, but you might want to do the same.)
Note that an "array" is just a long sequence of boxes that all
{*filter*}up against each other. A pointer is just a box containing
an arrow
Now draw more boxes for "char **". A "char **" is a pointer that
points to another pointer. We can make one that points to p2:
+---------+ +---------+
| q ------> | p2 |
+---------+ +---------+
What you have on paper now is a model for this:
char *p1 = "abc";
char *p2 = p1 + 3;
char **q = &p2;
Clearly this has not yet solved your problem, but at the same time,
you can see what "q" is *not* a "pointer to the first element of
an array" at all. On the other hand, it *could* be (or become)
such a pointer -- if only you had an array whose first element
was around to be pointed-to.
First, though, you will need to draw that array, of the kinds of
things that "char **" can point to, i.e., of "char *"s:
char *xx[3]; /* declare xx as array 3 of pointer to char */
+---------+---------+---------+
| xx0 | xx1 | xx2 |
+---------+---------+---------+
These three boxes have no initial values, so their arrows (they
are pointers so they hold arrows) are not pointing anywhere in
particular -- you need to set each one, just like you needed to
set p1 and p2 to point somewhere. But now you have the array --
the set of boxes in a row -- that "q" can point to, so now you
can point q to it:
q = &xx[0]; /* or q = xx */
If you then set each xx[i] to a valid pointer value -- draw some
more boxes on your paper as needed here -- then q will point to
the first of three pointers, each of which will point to a char,
or the first of some number of chars. Since q points to xx[0],
instead of saying xx[0] = "some valid value", you can also say:
q[0] = "some valid value".
Finally, as you are scribbling boxes on paper, remember two things:
- You "create" boxes by creating objects in memory.
- When you return from a function, all its automatic objects
("local variables") are destroyed. The value you return is
copied to a temporary box first, but any remaining boxes
that "belong to" that function should be scribbled-out on
the paper.
If you want a "permanent" box, you must use a variable that has
the static storage class -- such as one declared outside a function,
or declared using the "static" keyword within a function. In this
case you only get the one object, so a function that returns the
address of a "static" variable is somewhat dangerous. (The ANSI
C "ctime" function is like this -- it returns the address of some
static-duration array of "char"s. A later call to ctime() overwrites
the contents of the array, even if you are still using a pointer
to that array.)
If you want a box that is "semi-permanent" -- that lasts until you
explicitly say, "scribble this one out" -- you can use the malloc()
function. Calling malloc() gets you a box, or a sequence of adjacent
boxes, with no initial values as usual; it returns a pointer to
the box, or the first of the sequence of boxes. Those boxes are
good until you call free() and pass it the same value you got back
from malloc(). The free() function is what scribbles them out.
So, instead of "q = &xx[0]" to make q point to the first of three
boxes of the right size, you can also do:
q = malloc(3 * sizeof *q);
This asks for "3 boxes of the right size" and, as long as malloc()
itself has not run out of memory, gives you a pointer to the first
of those three. As with the xx[] array, those three boxes have no
initial values -- so you better give them some, perhaps using
malloc() again:
if (q == NULL) ... handle the error ...
q[0] = malloc(5 * sizeof *q[0]);
Note that always this fits the same pattern:
var = malloc(N * sizeof *var);
gets you N boxes of the right size. Leaving out N is the same
as using "1 *", i.e., gets you one box of the right size. As
always you must also check for NULL:
if (q[0] == NULL) ...
q[1] = malloc(8 * sizeof *q[1]);
if (q[1] == NULL) ...
q[2] = malloc(3 * sizeof *q[2]);
if (q[2] == NULL) ...
Of course, you can use a loop:
for (i = 0; i < 3; i++) {
q[i] = malloc(length[i] * sizeof *q[i]);
if (q[i] == NULL) ...
}
Note that I used a different length for each q[i] -- 5, 8, and 3
respectively -- so as long as malloc() does not run out of memory
and return NULL, each q[i] points to the first of a different number
of boxes. This means that q[i][j] is valid for different values
of j depending on the value of i. If you choose, you can instead
make each length the same. In this case, q will act just like a
"two-dimensional" array. If they are different, q will act like
a "ragged" array. Of course, on your paper -- you HAVE been drawing
all this, right? :-) -- you will always be able to see just how
much each q[i] points to. Unfortunately, there is no C function
to ask "how big is this thing I got from malloc()?". Instead, if
you need those numbers in your code (rather than on your paper),
you will have to record them yourself as you allocate them.
Finally, note that you can even sum up all the lengths you want
at once, and malloc() that much space in one swoop. If we want
"5, 8, and 3" we can just calculate 5+8+3 = 16, and do this:
q[0] = malloc(16 * sizeof *q[0]);
if (q[0] == NULL) ...
This time, we draw one 16-long-group of boxes on the paper.
Having done that, we can use pointer arithmetic to make q[1]
and q[2] point to places within this 16-long group, just as
p1 and p2 (remember them?) point into the same 4-element array
of "char"s. Just do:
q[1] = q[0] + 5;
q[2] = q[1] + 8;
and draw, on your paper, the appropriate arrows.
(If you actually sit down and do this exercise, your understanding
of C pointers will grow by leaps and bounds. If you just read this
article, it will not stick as well. Try it with pencil and paper
-- or marker and whiteboard, or whatever you have handy. It really
is a worthwhile exercise.)
--
In-Real-Life: Chris Torek, Wind River Systems (BSD engineering)
Salt Lake City, UT, USA (4039.22'N, 11150.29'W)
(you probably cannot email me -- spam has effectively killed email)