Reputation: 13
Consider this code:
static char a[2][2] = {
{ 1, 2 },
{ 3, 4 },
};
int main()
{
char **p = (char**)a; // needs cast, or compiler complains (which makes sense)
printf("%p\n", p);
printf("%p\n", &a[1][0]);
printf("%d\n", a[1][0]);
printf("%p\n", &p[1][0]); // why null? why doesn't compiler complain about this?
printf("%d\n", p[1][0]); // segfault, of course
return 0;
}
which yields this output:
0x804a018
0x804a01a
3
(nil)
Segmentation fault
I understand that an array can decay to a pointer. What I don't understand is why the compiler (g++) will let me try to do the reverse. If p is a char**, why does it let me use p[x][x] without so much as a warning? It obviously doesn't even come close to working, as the resulting pointer is null.
Incidentally, I am asking this about code from a 3rd party, which evidently works for them. (compiled in Windows, not with g++). So, I'm not looking for suggestions on how to fix this code, I already know how to do that. I just want to understand why the compiler doesn't complain, and why the result is a null pointer.
Thanks.
Upvotes: 0
Views: 459
Reputation: 110658
You simply cannot start treating a 2D array of char
like a char**
. In memory, the array looks something like this:
| 1 | 2 | 3 | 4 |
Each element follows the previous element. The array name will be implicitly converted to a pointer to its first element:
| 1 | 2 | 3 | 4 |
^
|
Now if you convert this pointer to a char**
, you're saying "If you dereference this pointer, you will find a char*
" which is an outright lie. If you dereference the pointer you will get a char
with value 1
, not a pointer at all.
Then when you do p[1][0]
, you are treating the value at p[1]
(which essentially moves the pointe p
along by sizeof(char*)
) as a pointer and trying to dereference it. Of course, this is leading you straight to undefined behaviour.
The compiler didn't let you do that cast because it was a silly cast to do. Don't do it. Just because a C-style cast allows you to do it, that doesn't mean it's an okay operation. A C-style cast will fall back to a reinterpret_cast
if no other cast works, in which case you're almost certainly going to hit undefined behaviour.
Upvotes: 6
Reputation: 66922
To actually give an answer: a
is an array. For a statement like char* p = a;
, a
would automatically decay to a pointer to the first element { 1, 2 }
, and since that's an array, that would also decay to it's first element 1
. However, with char**p = a
, a
still decays to that array of array of char
, and then you're casting that entire array to an array of pointers to chars
: (which it interprets as {0x01020304, 0x????????}
), which makes no sense at all. It's a pointer to an array, not a pointer to pointers. That's why you needed the cast, because it doesn't make sense.
Second, when you type p[1]
, it treats that data (and a few bytes after it) as if they were an array of char
pointers, {0x01020304, 0x00000000}
, and returns the second element. (We can see it's all zeros in this particular case, because that's what printed on the screen later), Then the [0]
dereferences that second mystery unknown pointer that happens to be NULL, giving you a segfault.
Upvotes: 2
Reputation: 40659
When you say this:
char a[2][2];
char **p = (char**)a;
that is a mistake.
a
is not an array of pointers to characters.
It is an array of storage blocks, each of which is an array of characters.
Upvotes: 0