To understand what is going on you should place a breakpoint on your first for
line, run the code till it stops and then step through watching the values of fruit
, x
& g
. If you do this you'll notice something strange - after the first time around the outer loop the first element of fruit
changes to banana
(the remaining entries stay the same, so you now have two bananas).
Do this now and watch. If you can't use the debugger stop now and figure it out.
So what is happening?
First let's look at what your code outputs:
ae?aa (? is actually upside down on my run)
banana
etc.
Where could those characters be coming from? Well if the compiler packs your strings into memory, remembering that each string ends in a null byte which we will represent by ?
then packed into memory you have:
Water?banana?pear?apple
etc. Your program is printing out 5 characters, the number in Water, and they are the 2nd, 4th, 6th, 8th and 10th in memory...
So now we know what we're seeing, why are we seeing it?
Let's look at fruit
, it is a 1-dimensional array of char *
- pointers into memory containing the characters in the strings. If we look at the first few pointers in this array when I run your code they are (actual values may differ):
0x100000e24
0x100000e2a
0x100000e31
Looking at just the last two digits, and converting from hexadecimal, we have 36, 42, 49. The first difference is 6, the number of bytes needed to store "Water"
(including the trailing null byte), the next is 7, the number of bytes needed to store "banana"
, and so it goes on.
Now let's look at your first for
loop:
for(x=0; *(fruit+x) != '\0'; x++)
Now *(fruit+x)
is the same as writing fruit[x]
, so in each iteration of the loop you are looking at an element of fruit
starting with the first (index 0). Now fruit
contains values of type char *
but you are comparing the value to a char
- they are not the same thing!
When C constructs an array with unspecified bounds (the empty []
in the declaration of fruit
) from a literal as you've done here it does not add anything after the last element of the array to indicate there are no more elements. There are two common ways to deal with this: you can calculate the number of elements using two calls to sizeof
(see @Lundin's answer); or you can use a sentinel - a value which will not occur in the array otherwise. For arrays of pointer values the standard sentinel is NULL
- the pre-declared pointer to nothing. So to make this part of your code work we change the code to:
char *fruit[] =
{
"Water",
"banana",
"pear",
"apple",
"coconut",
"grape",
"blueberry",
NULL
};
int x;
int g;
for(x=0; *(fruit+x) != NULL; x++)
This loop will now set x
to the values 0
through 6
.
Now let's look at your inner loop:
for (g=0; *(*fruit)++; g++)
To reduce the stars circling our heads, let's replace *first
by first[0]
:
for (g=0; *(fruit[0])++; g++)
So *(fruit[0])++
:
- Obtains the value in the first element of the array (
fruit[0]
), which is a pointer (char *
) to the first character of the string "Water"
;
- Indirects via that pointer (
*(fruit[0]
) to obtain a single character, which is W
;
- Implicitly compares that character to the null byte - the test part of the
for
; and
- Increments the pointer stored in the first element of the array
So after the first iteration of this loop the fruit
array contains (assuming values as above):
0x100000e25
0x100000e2a
0x100000e31
The first element has now changed to point at the a
in "Water"
. As you are reading the array to print it you are also changing it - probably not a good idea.
Also note that this loop always references the first element of the array, it never moves past it however many iterations of the outer loop occur - this loop is not stepping through the elements of fruit
either - to do that you would need to reference x
somewhere, which you do not in the for
itself, but you do in the body:
putchar(*(*(fruit+x)+g))
which re-written using indexing is:
putchar(fruit[x][g])
which outputs the g'th character of the x'th element of fruit
. This would make sense if the array fruit
was not being modified, however as we've just determined the first element of fruit
now references the a
in "Water"
, x
& g
are both zero, so this outputs a
and not W
as you hoped.
Now consider the second iteration, the for
loop examines the a
and find it is not the null byte, so fruit[0]
is incremented to reference the t
in "Water"
and g
is incremented to 1
. Now the putchar
looks up the first element of fruit
, which is referencing "ter..."
having been incremented twice, g
is 1
so the second character is selected, which is e
, and that is output.
In each iteration you are incrementing the pointer in fruit[0]
and incrementing g
, so the sum increments by 2 each time and the putchar
outputs the 2nd, 4th, 6th, 8th and 10th characters in memory... As the strings follow each other you get a
, e
and the null byte from "Water"
and two a
's from "banana"
.
As your program continues fruit[0]
marches through memory but as the putchar
uses x
and so references the unchanged pointers in fruit[1]
onwards the remaining strings, by luck, print out correctly. If you don't add the sentinel to the array the outer loop keeps marching through memory until a zero byte is found - so you may get garbage printed after blueberry
as well.
So how do you fix this?
Well you should never have been altering fruit[0]
in there first place, and you should be referencing x
in your inner for
to step through the array.
Keeping close to your original code a solution is to copy the pointer stored in fruit
into a local variable and increment that to walk through the string:
char *fruit[] =
{ "Water",
"banana",
"pear",
"apple",
"coconut",
"grape",
"blueberry",
NULL
};
int x;
int g;
for(x=0; *(fruit+x) != NULL; x++)
{
char *p = *(fruit+x); // copy the x'th pointer from fruit and store it in p
for (g=0; *(p+g); g++) // step through the string incrementing p (not the x'th element of fruit)
{
putchar(*(p+g));
}
putchar('\n');
}
HTH