tags:

views:

498

answers:

5
void main()
{
    char str[2][7] = {"1234567", "abcdefg"};
    char** p = str;
    printf("%d\n", *(p+1));
    printf("%c\n", *(p+1));
}

The output is:

1631008309
5

Edit: Thank you. I see the '5' is only 0x35, other than str[0][4] I supposed to be. Why can't I get out str[0][4] instead of this strange 1631008309??

OMG, I'm foolish enough to ask this question! Thank you all, guys.

+5  A: 

This line:

char** p = str;

makes no sense. str is an array of 2 arrays of 7 chars. When used in an expression context, it evaluates to a pointer to its first element, equivalent to:

&str[0]

which has type "pointer to array of 7 chars". That is in no way similar to a "pointer to pointer to char", which is the type of p. Any of these would make sense instead:

char *p = str[0];

char *p = str[1];

char (*p)[7] = str;
caf
+1 You're right, but that was not the question
tuergeist
Yeah I guess I read it as "Why is the program generating this output instead of the output I expect".
caf
+4  A: 

Using %d does not convert an ASCII string of alphanumerics into a number. Using %d means it expects that the value found will be interpreted as a binary representation of the number and converted TO ASCII for printing.

kmarsh
+9  A: 

You're pointing a char** at the beginning of the memory allocated to your 2-d array.

When you add 1 to a pointer it moves you along by the sizeof the type pointed to, in this case the sizeof a char *, which is evidently 4 bytes in your system. Then you're dereferencing it and treating the result as an int (%d), which gives you the a765 I mentioned in my comment to unwind. When you dereference it and treat it as a char you correctly get 5.

[I see that unwind has deleted their answer, so just for completeness I'll add that the "a765" is the ASCII interpretation of the larger number you get (0x61373635).]

Vicky
+1. Excellent, and spot-on.
DevSolar
A: 

I understand your confusion, the two statements is exactly the same.

Both statements cast *(p + 1) to an pointer to an integer prior dereferencing it, the default is to cast everything to an integer in a va_arg argument.

printf then truncates the integer to a char prior printing it in the second printf statement.

This code snippet makes it more clear what is going on:

void main()
{
  char str[2][7] = {"1234567", "abcdefg"};
  char** p = str;
  printf("%d\n", *(p+1));
  printf("%c\n", *(p+1));
  int n = *(p+1);
  char c = *(p+1);
  printf("0x%08X\n", n);
  printf("0x%08X\n", c);
}

Which outputs:

1631008309
5
0x61373635
0x00000035

Type safety in stdarg is explained here: stdarg

Ernelli
+3  A: 

Note that the C standard defines that the main() program returns an int, not void.

Given the code:

#include <stdio.h>
int main()
{
    char str[2][7] = {"1234567", "abcdefg"};
    char** p = str;
    printf("%d\n", *(p+1));
    printf("%c\n", *(p+1));
    return(0);
}

When compiled withgcc -o x -Wall x.c, you get the warnings:

x.c: In function ‘main’:
x.c:5: warning: initialization from incompatible pointer type
x.c:6: warning: format ‘%d’ expects type ‘int’, but argument 2 has type ‘char *’
x.c:7: warning: format ‘%c’ expects type ‘int’, but argument 2 has type ‘char *’

(If you omit the -Wall, you don't get the format warnings.)

This tells you that you are passing a pointer to printf(). Printing a pointer is not going to be all that useful. However, the previous warning also tells you that you are mis-initializing the variable p. However, the net result is that p points to the start of the space in str. When you print

One interesting little quirk: normally, a string such as "1234567" includes a terminating NUL '\0'; in your arays, because you specified that the length of the array is 7, not 8, the terminating null is missing. Be careful how you print the strings!

Here's another variant of the code:

#include <stdio.h>
int main()
{
    char str[2][7] = {"1234567", "abcdefg"};
    char** p = str;
    printf("%d\n", *(p+1));
    printf("%c\n", *(p+1));
    printf("%p %p %p -- %p %p %p\n", str, &str[0], &str[0][0], p, p+1, *(p+1));
    printf("%.4s\n", (p+1));
    return(0);
}

That gives me the following output (from an Mac):

1631008309
5
0xbffffa5e 0xbffffa5e 0xbffffa5e -- 0xbffffa5e 0xbffffa62 0x61373635
567a

Note that the addresses str, &str[0] and &str[0][0] all have the same value, and that p+1 is four bytes further along. When treated as a string, it prints the last three bytes of the first initializer and the first byte of the second.

Also, for fun, compiled with gcc -m64 -o x64 x.c, the output is:

1701077858
b
0x7fff5fbff9e0 0x7fff5fbff9e0 0x7fff5fbff9e0 -- 0x7fff5fbff9e0 0x7fff5fbff9e8 0x676665646362
bcde
Jonathan Leffler