我有一个问题,一个点到结构数组的指针。
我声明并初始化了三个指向结构体的指针,这个结构体有一个成员。val
. 我创建了一个指针 arr
到一个指向结构指针的数组。然后我创建了一个指针 pa
指针 arr
和一个指针 pa1
到 pa
.
当我试图提取第二个元素的 arr
使用 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;
}
然后我得到的结果如下。
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
问题是,为什么 2nd val
的 pa1
不是 3 不过 0?
都有 pa
和 pa1
似乎指向同一个地址 0000000000e761e4
.
更新
该行 // modified
应是 std::printf( "2nd pos: %p\n", *(pa+1) );
.
结果如下
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
该 2nd pos
的 pa
和 pa1
其实是不一样的。
现在的问题是 如何从 arr
使用 pa1
?
你必须做一个图,什么是存储在哪里,如何存储。
例如:
// 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
但是,如果你这样做:
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
顺便说一下,你可以用调试器和变量表来跟踪发生了什么,什么东西指向哪里,以及它的值。
EDIT:添加了更大的结构,所以差异更加明显。
#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.
}
还有输出
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
都是 pa
和 pa1
似乎指向同一个地址 0000000000e761e4
.
但是... 它们是不同类型的。因此 +1
给出了一个不同的地址。
pa1
属于 a*
因此 pa1+1
是 reinterpret_cast<a*>(reinterpret_cast<unsigned char*>(pa1)+sizeof(a))
. 请注意,我们去掉了一层计算偏移量的间接性。
pa
的类型是 a**
因此 pa1+1
是 reinterpret_cast<a**>(reinterpret_cast<unsigned char*>(pa)+sizeof(a*))
.
由于 reinterpret_cast<unsigned char*>(pa)==reinterpret_cast<unsigned char*>(pa1)
但 sizeof(a)!=sizeof(a*)
我们有不同的结果。
事情是这样的 pa1
的数组中的第一个地址不是 a
! 因此,这个计算是错误的。