printing array of pointers


A

arnuld

Its an array of pointers (to chars). Program compiles fine

PURPOSE: To print the values that array elements point to.
CURRENT STATE: Nothing is getting printed.



#include <stdio.h>


void swap_elements(char* , char* );
void print_array(char** p);

int main(void)
{
char* arrp[4] = {0};

char a = 'a';
char b = 'b';
char c = 'c';

char* pa = &a;
char* pb = &b;
char* pc = &c;

arrp[1] = pa;
arrp[2] = pb;
arrp[3] = pc;


print_array(arrp);

/* printf("Swapping Elements ....\n");
swap_elements(arrp[1], arrp[3]);
print_array(arrp);
*/

return 0;
}


void swap_elements(char* p, char* q)
{
return;
}


void print_array(char** arr)
{
char* p;

for( p = *arr; p; ++p)
{
printf("Element: %c\t", *p);
}

printf("\n");

return;
}


======================= OUTPUT ==========================
[[email protected] programs]$ gcc -ansi -pedantic -Wall -Wextra test.c
test.c:35: warning: unused parameter ‘p’
test.c:35: warning: unused parameter ‘q’
[[email protected] programs]$ ./a.out

[[email protected] programs]$
 
Ad

Advertisements

S

Seebs

PURPOSE: To print the values that array elements point to.
CURRENT STATE: Nothing is getting printed.
Wow.

int main(void)
{
char* arrp[4] = {0};

char a = 'a';
char b = 'b';
char c = 'c';

char* pa = &a;
char* pb = &b;
char* pc = &c;

arrp[1] = pa;
arrp[2] = pb;
arrp[3] = pc;

....

Okay, so you have an array, arrp. It has four members, number 0, 1, 2, and 3.
After all of this, it's sort of like:

char *arrp[4] = { 0, pa, pb, pc };
print_array(arrp);
void print_array(char** arr)
{
char* p;
for( p = *arr; p; ++p)
{
printf("Element: %c\t", *p);
}

Okay. *arr is the same as arrp[0], so that's 0. So the for loop
terminates immediately.

You almost certainly don't mean what you're saying here, which is "set p to be
the first pointer in arr. As long as p is not a null pointer, increment p."

The reason this is probably not what you want is that, if you happened to get
a non-null pointer, there's no particular reason to think that ++p will ever
make p into a null pointer -- certainly not before something horrific happens.

What you probably meant was:

void print_array(char **arr) {
while (arr && *arr) {
printf("Element: %s\t", *arr);
++arr;
}
printf("\n");
}

This would print the strings... oh, wait, your array doesn't HAVE strings,
it has pointers to individual characters.

So let's go back up to the top, and try

char *arrp[4] = { "a", "b", "c", 0 };

note:

* Arrays start at 0, not 1. The four members of arrp are 0, 1, 2, and 3.
* A pointer to a character is not a string. In most cases, declaring
individual char variables and taking their addresses is not useful.

I'd recommend you get a C book (I'm a big fan of Kim King's "C Programming: A
Modern Approach") and work along with the provided exercises for a bit.

-s
 
A

arnuld

Okay. *arr is the same as arrp[0], so that's 0. So the for loop
terminates immediately.

Yes, I realized that after I posted it. I changed the assignments to
0,1,2 from 1,2,3.


You almost certainly don't mean what you're saying here, which is "set p
to be the first pointer in arr. As long as p is not a null pointer,
increment p."
The reason this is probably not what you want is that, if you happened
to get a non-null pointer, there's no particular reason to think that
++p will ever make p into a null pointer -- certainly not before
something horrific happens.

That is the part I did not get, not yet.

p = *arr;
printf("Element: = *p\n", *p);

p will point to the first element of arr, next statement will print the
contents of first element and after doing a ++p, it will
point to the next and will print that element.

When p will point to the last element of the array (NULL) then *p will
give segfault. I mean every time, after printing the first 3 elements, it
will give segfault, a perfect pattern of segfault must be there but it is
not there. This is what bugging me.



What you probably meant was:

void print_array(char **arr) {
while (arr && *arr) {
printf("Element: %s\t", *arr);
++arr;
}
printf("\n");
}


Its a pointer to pointer, which means I am using the original pointer and
by doing ++arr, you are moving the original pointer which I don't want,
because I want to use it later in the program. Can you do that by not
using the original pointer ?

This would print the strings... oh, wait, your array doesn't HAVE
strings, it has pointers to individual characters.

So let's go back up to the top, and try

char *arrp[4] = { "a", "b", "c", 0 };

note:

I wonder why you give an example of strings, how did it relate to my
problem ?
 
A

arnuld

for( p = *arr; p; ++p)
{
printf("Element: %c\t", *p);
}


If I change that for loop check to:

for( p = *arr; *p; ++p)

Shouldn't it stop because *p == NULL (last element of the array). But it
does not happen. Why ?
 
A

arnuld

void print_array(char **arr) {
while (arr && *arr) {
printf("Element: %s\t", *arr);
++arr;
}
printf("\n");
}


I wonder why you used %s, instead of %c:

void print_array(char** arr)
{
for( ; arr && *arr && **arr; ++arr)
{
printf("Element: %c\t", **arr);
}

printf("\n");

return;
}



OR


void print_array(char** arr)
{
for( ; arr && *arr; ++arr)
{
printf("Element: %c\t", **arr);
}

printf("\n");

return;
}



Why will you prefer one over the other ?
 
M

Mark

arnuld said:
I wonder why you used %s, instead of %c:
'arr' is in fact an array of strings, so %s is relevant specifier, while %c
would be used to print one single character.
Correct me if I'm wrong.
 
Ad

Advertisements

A

arnuld

You're right. arr is char **, so *arr is char *, and (assuming it really
is a string, not just a pointer to arbitrary characters) %s is fine for
printing it. To print a single character, one would use %c for the
format specifier and **arr as the argument expression.

A string is an array of characters, whereas I have an array of pointers
to characters.

*arr == arr[0] == char* = 'a';

I thought %s expects something like "a" which I don't have. I am confused
on what is going on.
 
B

bartc

arnuld said:
Its an array of pointers (to chars). Program compiles fine

PURPOSE: To print the values that array elements point to.
CURRENT STATE: Nothing is getting printed.
char* arrp[4] = {0};
arrp[1] = pa;
arrp[2] = pb;
arrp[3] = pc;

Try zero-based indexing, 0 to 2. Put a NULL value in arrp[3];
void print_array(char** arr)
{
char* p;

for( p = *arr; p; ++p)
{
printf("Element: %c\t", *p);
}
I had to use the following to get it to work. But it depends on what your
terminating conditioning was supposed to be. Here I assumed the last
element of arrp was NULL.

char** p;

for( p = arr; *p; ++p)
{
printf("Element: %c\t", **p);
}


It was also clearer written explicitly as an array rather than pointers:

void print_array(char* arr[])
{
int i;

for( i=0; arr; ++i)
{
printf("Element: %c\n", *arr);
}

In which case you can also pass an array length rather then use a sentinel.
 
B

Ben Bacarisse

arnuld said:
A string is an array of characters, whereas I have an array of pointers
to characters.

Those pointers might point to the start of strings. Nothing in your
sentence decides the matter either way. if the pointer points at a
character that part of any array which includes a '\0' character, then
you have a string.
*arr == arr[0] == char* = 'a';

I don't follow this, sorry.
I thought %s expects something like "a" which I don't have. I am confused
on what is going on.

The trouble is there are two program being discussed. Yours has no
strings in the array; Seebs altered it so that it did have strings
rather than single characters. That is why he switched to %s.
 
B

Ben Bacarisse

arnuld said:
That is the part I did not get, not yet.

p = *arr;
printf("Element: = *p\n", *p);

There at least one typo there, so let's go back to what you had:

| void print_array(char** arr)
| {
| char* p;
| for( p = *arr; p; ++p)
| {
| printf("Element: %c\t", *p);
| }
| printf("\n");
| return;
| }
p will point to the first element of arr, next statement will print the
contents of first element and after doing a ++p, it will
point to the next and will print that element.

No. p points to a char. Initially (once you've corrected the 1
index to 0) that is the 'a' in main's object called a. Doing ++p
makes p point to some mysterious place just after that 'a'. That's
legal (one past an object is a valid pointer) but you can't
dereference that pointer. Continuing to look at characters beyond the
'a' is bound to wrong at some time, but it is UB on the second iteration.
When p will point to the last element of the array (NULL) then *p will
give segfault. I mean every time, after printing the first 3 elements, it
will give segfault, a perfect pattern of segfault must be there but it is
not there. This is what bugging me.

I am not sure what you mean by this. When I correct the indexes, your
program prints random memory contents after printing the 'a'. What
you are seeing may be misleading you about what the program does.

I wonder why you give an example of strings, how did it relate to my
problem ?

I suspect he wanted to show what he thought you meant. The smallest
change to your test that does what you want is, in fact:

arrp[0] = pa;
arrp[1] = pb;
arrp[2] = pc;

and later:

void print_array(char** arr)
{

for(; *arr; arr++)
{
printf("Element: %c\t", **arr);
}

printf("\n");

return;
}

Does that small change help you see what your original was doing?
 
A

arnuld

There at least one typo there, so let's go back to what you had:

| void print_array(char** arr)
| {
| char* p;
| for( p = *arr; p; ++p)
| {
| printf("Element: %c\t", *p);
| }
| printf("\n");
| return;
| }

No. p points to a char.

**arr is a char, not *arr. See down before you reply.


Initially (once you've corrected the 1 index
to 0) that is the 'a' in main's object called a. Doing ++p makes p
point to some mysterious place just after that 'a'.

p = *arr;

if p points to first element of the array, why ++p can't point to second
element of the same array ?



I suspect he wanted to show what he thought you meant. The smallest
change to your test that does what you want is, in fact:

arrp[0] = pa;
arrp[1] = pb;
arrp[2] = pc;

and later:

void print_array(char** arr)
{

for(; *arr; arr++)
{
printf("Element: %c\t", **arr);
}

printf("\n");

return;
}

Does that small change help you see what your original was doing?


Nope, I am still confused, like you use %c for **arr, which means **arrc
is a char while *arrc is not, then how can /p = *arrc/ can point to a
char ?
 
Ad

Advertisements

A

arnuld

No. p points to a char.


That is exactly where things became a mess. Seems like I can't
comprehend /p points to a char/ but I can easily comprehend /p is a
pointer to char/. By former I always thought p itself *is* the char, I
never done any mistake in understanding the real meaning of later.

Eh.. why it is so difficult and messy to get-off from the common-sense
thinking of humans to /zero common-sense, only logical thinking/ of a
compiler and then switch back.
 
B

Ben Bacarisse

arnuld said:
**arr is a char, not *arr. See down before you reply.

Yes, **arr is a char. *arr is a char *, so p = *arr set the char *
called p to point to the same place that *arr points.
p = *arr;

if p points to first element of the array, why ++p can't point to second
element of the same array ?

It does. What is that array? If *p is the 'a' inside main's a
object, then ++p points just after that declared variable. You can't
do anything with that pointer. It most certainly does not point to
the 'b' except, possibly, by accident.
I suspect he wanted to show what he thought you meant. The smallest
change to your test that does what you want is, in fact:

arrp[0] = pa;
arrp[1] = pb;
arrp[2] = pc;

and later:

void print_array(char** arr)
{

for(; *arr; arr++)
{
printf("Element: %c\t", **arr);
}

printf("\n");

return;
}

Does that small change help you see what your original was doing?


Nope, I am still confused, like you use %c for **arr, which means **arrc
is a char while *arrc is not, then how can /p = *arrc/ can point to a
char ?

You yourself said **arr was of type char (and you were right). That's
why you are correct to use %c to print it. I just copied that. p
points to a char because *arr and p are both of type char *. **arr
and *p denote the same character.
 
S

Seebs

p = *arr;
printf("Element: = *p\n", *p);

p will point to the first element of arr, next statement will print the
contents of first element and after doing a ++p, it will
point to the next and will print that element.

Well, you probably want a "%s" or something in the format string, but...
When p will point to the last element of the array (NULL) then *p will
give segfault. I mean every time, after printing the first 3 elements, it
will give segfault, a perfect pattern of segfault must be there but it is
not there. This is what bugging me.

It's pretty obvious. If *p is null, then you're trying to dereference
that null pointer.

This handles that by aborting when *p (I spelled it *arr here) is null.
Its a pointer to pointer, which means I am using the original pointer and
by doing ++arr, you are moving the original pointer which I don't want,
because I want to use it later in the program. Can you do that by not
using the original pointer ?

Again, please consult a very beginning-level book on C. I am not moving
the original pointer. I'm moving the copy of it which was passed as an
argument.

C passes arguments by value, not by reference.
I wonder why you give an example of strings, how did it relate to my
problem ?

Convention; if you have a char**, you presumably want the individual
components to be char *, and by strong convention in C, "char *" is usually
used to point to strings.

-s
 
S

Seebs

If I change that for loop check to:

for( p = *arr; *p; ++p)

Shouldn't it stop because *p == NULL (last element of the array). But it
does not happen. Why ?

Because you're not counting through the elements of arr, you're counting
from the first element to further pointers.

Okay, imagine that we look in a debugger, and arr[] actually holds the
values:

{ 0x10000, 0x10100, 0x10200, 0x0 }

My loop iterates:
0x10000, 0x10100, 0x10200, 0

Your loop iterates:
0x10000, 0x10001, 0x10002, 0x10003, 0x10004, 0x10005, 0x10006,
0x10007, 0x10008, 0x10009, ...

-s
 
A

arnuld

Because you're not counting through the elements of arr, you're counting
from the first element to further pointers.

Okay, imagine that we look in a debugger, and arr[] actually holds the
values:

{ 0x10000, 0x10100, 0x10200, 0x0 }

My loop iterates:
0x10000, 0x10100, 0x10200, 0

Your loop iterates:
0x10000, 0x10001, 0x10002, 0x10003, 0x10004, 0x10005, 0x10006,
0x10007, 0x10008, 0x10009, ...


Yes, I did get it now that my local pointer is not iterating in the array
but at some random locations.

Look at this code:



#include <stdio.h>

void print_arrc(char* );
void print_arrp(char** );

int main(void)
{
char arrc[] = "comp";
char* arrp[5] = {0};

char a = 'a';
char b = 'b';
char c = 'c';
char d = 'd';

arrp[0] = &a;
arrp[1] = &b;
arrp[2] = &c;
arrp[3] = &d;

print_arrc(arrc);
print_arrp(arrp);

return 0;
}


void print_arrc(char* p)
{
for( ; p && *p; ++p )
{
printf("Char Element: %c\n", *p);
}

printf("\n\n");
}


void print_arrp(char** arr)
{
for( ; arr && *arr; ++arr)
{
printf("Points to Element: %c\n", **arr);
}

printf("\n\n");

return;
}

======================== OUTPUT =====================
[[email protected] programs]$ gcc -ansi -pedantic -Wall -Wextra test.c
[[email protected] programs]$ ./a.out
Char Element: c
Char Element: o
Char Element: m
Char Element: p


Points to Element: a
Points to Element: b
Points to Element: c
Points to Element: d


[[email protected] programs]$







Now that iteration works fine. The problem (which I was unable to
understand) was because of the extra pointers I introduced ?

pa = &a;
arr[0] = pa;

instead of

arr[0] = &a;


Is that /pa/ the reason that I was getting that weired pointer iteration ?
 
Ad

Advertisements

B

Ben Bacarisse

arnuld said:
char* arrp[5] = {0};

char a = 'a';
char b = 'b';
char c = 'c';
char d = 'd';

arrp[0] = &a;
arrp[1] = &b;
arrp[2] = &c;
arrp[3] = &d;
print_arrp(arrp);

void print_arrp(char** arr)
{
for( ; arr && *arr; ++arr)
{
printf("Points to Element: %c\n", **arr);
}
printf("\n\n");
return;
}

Now that iteration works fine. The problem (which I was unable to
understand) was because of the extra pointers I introduced ?

pa = &a;
arr[0] = pa;

instead of

arr[0] = &a;

Is that /pa/ the reason that I was getting that weired pointer
iteration ?

No, that makes no difference. You were getting what you call weird
iteration because the old loop you had incremented the wrong pointer.
The new loop increments the right thing.

To understand your code you need to look at the two loops (the old and
the new one) and see why they do different things. One increments a
copy of *arr called p and the other increments arr.
 
S

Seebs

On Tue, 27 Oct 2009 15:28:27 +0000, Seebs wrote:
Because you're not counting through the elements of arr, you're counting
from the first element to further pointers.

Okay, imagine that we look in a debugger, and arr[] actually holds the
values:

{ 0x10000, 0x10100, 0x10200, 0x0 }

My loop iterates:
0x10000, 0x10100, 0x10200, 0

Your loop iterates:
0x10000, 0x10001, 0x10002, 0x10003, 0x10004, 0x10005, 0x10006,
0x10007, 0x10008, 0x10009, ...
Yes, I did get it now that my local pointer is not iterating in the array
but at some random locations.

Not random.

Not at all random.

Very specific, 100% predictable. You're iterating from the first pointer in
the array through the memory after it.
Now that iteration works fine. The problem (which I was unable to
understand) was because of the extra pointers I introduced ?

No. It was because you were obtaining the contents of the first member
of the array, and iterating from it to pointers higher than it, rather
than iterating through your array.
pa = &a;
arr[0] = pa;

instead of

arr[0] = &a;

Is that /pa/ the reason that I was getting that weired pointer iteration ?

No. Those two versions are completely interchangeable.

Slow down! You need to develop a better working theory of what pointers
are. You're having a lot of trouble getting confused because you don't
seem clear on the difference between the pointer and the thing it points
to.

-s
 
A

arnuld

No, that makes no difference. You were getting what you call weird
iteration because the old loop you had incremented the wrong pointer.
The new loop increments the right thing.

To understand your code you need to look at the two loops (the old and
the new one) and see why they do different things. One increments a
copy of *arr called p and the other increments arr.

Here is very simplified version of the above code which prints the same
weired iteration. Look at the 2 printing functions:




#include <stdio.h>


void print_arrc(char* );
void print_arrp(char** );

int main(void)
{
char arrc[] = "comp";
char* arrp[5] = {0};

char a = 'a';
char b = 'b';
char c = 'c';

arrp[0] = &a;
arrp[1] = &b;
arrp[2] = &c;


print_arrc(arrc);
print_arrp(arrp);


return 0;
}


void print_arrc(char* p)
{
char* c = p;
for( ; c && *c; ++c )
{
printf("Char Element: %c\n", *c);
}

printf("\n\n");
}


void print_arrp(char** arr)
{
char* c = *arr;
for( ; c && *c; ++c)
{
printf("Points to Element: %c\n", *c);
}

printf("\n\n");

return;
}



Inside the print function: it works fine by taking a local pointer to
the /array of chars/ passed as argument

Inside the print function: it does not work by taking a local pointer to
the /array of pointers/ passed as argument


So an /array of char/ and an /array of pointers/ behave differently when
passed as arguments and accessed using local pointers.


Anyway, I don't see any point in using a local pointer as the passed
double-pointer is also local to the function. Trouble is I don't
understand the /Why/
 
Ad

Advertisements

B

Ben Bacarisse

arnuld said:
Here is very simplified version of the above code which prints the same
weired iteration. Look at the 2 printing functions:

I've removed the one that is not an issue:
#include <stdio.h>

void print_arrp(char** );

int main(void)
{
char arrc[] = "comp";
char* arrp[5] = {0};

char a = 'a';
char b = 'b';
char c = 'c';

arrp[0] = &a;
arrp[1] = &b;
arrp[2] = &c;

print_arrp(arrp);

return 0;
}

void print_arrp(char** arr)
{
char* c = *arr;
for( ; c && *c; ++c)
{
printf("Points to Element: %c\n", *c);
}
printf("\n\n");
return;
}

Inside the print function: it works fine by taking a local pointer to
the /array of chars/ passed as argument

Inside the print function: it does not work by taking a local pointer to
the /array of pointers/ passed as argument

So an /array of char/ and an /array of pointers/ behave differently when
passed as arguments and accessed using local pointers.

Anyway, I don't see any point in using a local pointer as the passed
double-pointer is also local to the function. Trouble is I don't
understand the /Why/

You are getting hung up one the two stars. First, you'd have no
trouble if you had to print a zero-terminted array on ints:

void print_arrp(int *arr)
{
for (; arr; ++arr)
printf("%d\n", *arr);
}

would you? You certainly wouldn't introduce a new local int and try
to iterate that, would you?

int p = *arr; /* wrong */
for (; p; ++p) /* wrong */
printf("%d\n", p); /* wrong */

and the compiler would complain if you tried to print *p rather than
p. Now, so we don't get two stars, lets pretend we have an array of
"strings":

typedef char *string;

An array of these is passed as a "string *" and we iterate it exactly
like the int array, but we can't use %d any more, we need the %s
format:

void print_arrp(string *arr)
{
for (; arr; ++arr)
printf("%s\n", *arr);
}

yes? Now, finally, the only different is that you don't want to print
all the characters pointer to by each element of the array, you want
only the first. You do this but using %c and printing either **arr
which looks odd now because you can't see the a string is pointer
anymore:

void print_arrp(string *arr)
{
for (; arr; ++arr)
printf("%c\n", **arr);
}

Put "char *" back instead of "string" and you get the correct code
(all that changes is the function heading). Your "weird code" did
this:

void print_arrp(string *arr)
{
string p = arr[0]; /* wrong */
for (; p; ++p) /* wrong */
printf("%c\n", *p); /* wrong */
}

I hope that helps.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top