Reputation: 5
I have a problem with a point to a pointer to an array of structures.
I declare and initialize three points to structure which has a member val
.
I create a pointer arr
to an array of pointer to structure.
Then I create a pointer pa
to pointer arr
and a pointer pa1
to pa
.
I encounter the problem when I try to extract the second element of the arr
using pa1
.
Main.cpp
#include<iostream>
#include<cstdio>
struct a{
int val;
};
int main(){
// create structures
a *a1 = new a;
a1->val = 5;
a *a2 = new a;
a2->val = 3;
a *a3 = new a;
a3->val = 4;
a *arr[3] = { a1, a2, a3 };
a **pa = arr;
std::cout << "Using pa:\n";
std::printf( "1st val: %d\n", (*(pa+0))->val );
std::printf( "1st pos: %p\n", (*(pa+0)) );
std::printf( "2nd val: %d\n", (*(pa+1))->val );
std::printf( "2nd pos: %p\n", (*(pa)+1) ); // modified
std::cout << std::endl << std::endl;
// a pointer to pa's value
a *pa1 = *pa;
std::cout << "Using pa1:\n";
std::printf( "1st val: %d\n", pa1->val );
std::printf( "1st pos: %p\n", pa1 );
std::printf( "2nd val: %d\n", (pa1+1)->val );
std::printf( "2nd pos: %p\n", pa1+1 );
delete a1, a2, a3;
return 0;
}
Then I get the result as below,
Using pa:
1st val: 5
1st pos: 0000000000e761e0
2nd val: 3
2nd pos: 0000000000e761e4
Using pa1:
1st val: 5
1st pos: 0000000000e761e0
2nd val: 0
2nd pos: 0000000000e761e4
The question is why the 2nd val
of pa1
is not 3 but 0?
Both pa
and pa1
seem to point to the same address 0000000000e761e4
.
UPDATE
The line // modified
should be std::printf( "2nd pos: %p\n", *(pa+1) );
.
And the result becomes as below
Using pa:
1st val: 5
1st pos: 0000000000ee61d0
2nd val: 3
2nd pos: 0000000000ee61f0
Using pa1:
1st val: 5
1st pos: 0000000000ee61d0
2nd val: 0
2nd pos: 0000000000ee61d4
The 2nd pos
of pa
and pa1
is actually not the same.
Now the question is How can I get the second element from arr
using pa1
?
Upvotes: 0
Views: 919
Reputation: 3739
You have to make a diagram, what is stored where and how:
For example:
// Allocated values (they are completely unrelated and may point into different locations):
a1 = 0x11111100;
a2 = 0x22222200;
a3 = 0x33333300;
// Where each of those addresses points to the place in memory with one constructed element of a
// next is creating arr of three pointers to a:
a * arr[3] = { a1, a2, a3 };
// which is effectively:
a * arr[3] = { 0x11111100, 0x22222200, 0x33333300};
// the arr is stored somewhere too, and it contains location, where those three addresses are stored
arr = 0xFFFFF340; // for example
// so when you do:
a ** pa = arr;
// then
pa == 0xFFFFF340;
// and pa + 1 == 0xFFFFF344 (increment depends on architecture)
// ==> dereferenced value is location where pointer to a2 is stored
However, if you do:
a * pa1 = *pa;
// then it means you did:
a * pa1 = arr[0];
// therefore:
pa1 == 0x11111100;
// and pa1+1 is memory location after a1 object eg.:
pa1+1 == 0x11111104;
// but as you've created only one element in that place, dereferencing it causes buffer overflow and using uninitialized memory
BTW: you could use debugger and variable watches to track what is happening and what is pointing where and its values
EDIT: Added bigger structs, so differences are much more obvious:
#include <stdio.h>
struct Data {
int x[100] = {0};
};
int main()
{
Data * a = new Data[10];
Data * b = new Data[5];
Data * c = new Data[1];
printf("a value: %p\n", a);
printf("b value: %p\n", b);
printf("c value: %p\n", c);
Data * arr[] = { a, b, c};
for (auto const& ptr : arr)
{
printf("Arr: element addr: %p position: %ld ptr to: %p\n", &ptr, &ptr-arr, ptr);
}
Data ** pa = arr;
printf("pa value: %p dereferenced value: %p\n", pa, *pa);
printf("pa+1 value: %p dereferenced value: %p\n", pa+1, *(pa+1));
printf("pa+2 value: %p dereferenced value: %p\n", pa+2, *(pa+2));
Data * pa0 = *pa;
printf("pa0 value: %p\n", pa0);
printf("pa0+1 value: %p\n", pa0+1);
printf("pa0+2 value: %p\n", pa0+2);
// cleanup - eventually :D But using std::unique_ptr would be much better.
}
And the output:
a value: 0x55bd5d988eb0
b value: 0x55bd5d989e60
c value: 0x55bd5d98a640
Arr: element addr: 0x7ffc528dcf00 position: 0 ptr to: 0x55bd5d988eb0
Arr: element addr: 0x7ffc528dcf08 position: 1 ptr to: 0x55bd5d989e60
Arr: element addr: 0x7ffc528dcf10 position: 2 ptr to: 0x55bd5d98a640
pa value: 0x7ffc528dcf00 dereferenced value: 0x55bd5d988eb0
pa+1 value: 0x7ffc528dcf08 dereferenced value: 0x55bd5d989e60
pa+2 value: 0x7ffc528dcf10 dereferenced value: 0x55bd5d98a640
pa0 value: 0x55bd5d988eb0
pa0+1 value: 0x55bd5d989040
pa0+2 value: 0x55bd5d9891d0
Upvotes: 1
Reputation:
Both pa
and pa1
seem to point to the same address 0000000000e761e4
.
But they are of different type. Therefor the +1
gives a different address.
pa1
is of type a*
, therefor the pa1+1
is reinterpret_cast<a*>(reinterpret_cast<unsigned char*>(pa1)+sizeof(a))
. Note we stripped one layer of indirection for calculating the offset.
pa
is of type a**
, therefor the pa1+1
is reinterpret_cast<a**>(reinterpret_cast<unsigned char*>(pa)+sizeof(a*))
.
Since reinterpret_cast<unsigned char*>(pa)==reinterpret_cast<unsigned char*>(pa1)
, but sizeof(a)!=sizeof(a*)
we have different results.
The thing is that pa1
is not the first address in an array of a
! Therefor this calculation is wrong.
Upvotes: 0