user2100564
user2100564

Reputation: 13

array indexing from a pointer

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

Answers (3)

Joseph Mansfield
Joseph Mansfield

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

Mooing Duck
Mooing Duck

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

Mike Dunlavey
Mike Dunlavey

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

Related Questions