从指向结构数组的指针中提取元素
Posted
技术标签:
【中文标题】从指向结构数组的指针中提取元素【英文标题】:extract elements from a pointer to pointer to array of structures 【发布时间】:2020-06-17 05:16:28 【问题描述】:我有一个指向结构数组的指针的问题。
我声明并初始化三个点到具有成员val
的结构。
我创建了一个指向结构指针数组的指针arr
。
然后我创建一个指针pa
指向指针arr
和一个指针pa1
指向pa
。
当我尝试使用pa1
提取arr
的第二个元素时遇到问题。
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
问题是为什么pa1
的2nd val
不是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
pa
和pa1
的2nd pos
实际上并不相同。
现在的问题是如何使用pa1
从arr
获取第二个元素?
【问题讨论】:
我认为你不能delete pa1;
和其他人,就此而言。
pa1+1
是 (*pa)+1
。为什么你认为这与*(pa+1)
相同?
让你感到困惑的是 std::printf( "2nd pos: %p\n", ((*pa)+1) );这应该是 std::printf("2nd pos: %p\n", (*(pa+1)) );以匹配您在上面的行中的内容。这将表明它们没有指向同一个地址。
@SouravGhosh 这些删除都无效(也许 pa1 删除了 a1)。他们都试图删除堆栈变量arr
。并且分配的a1
..a3
仍然存在。
@CraigR 谢谢。我已经更新了我的问题。
【参考方案1】:
你必须做一个图表,什么存储在哪里以及如何存储:
例如:
// 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
顺便说一句:您可以使用调试器和变量监视来跟踪正在发生的事情以及指向何处及其值的内容
编辑:添加了更大的结构,因此差异更加明显:
#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
【讨论】:
a *pa1 = *pa;
正好指向pa
指向的内容,也就是a1
。所以当我执行pa+1
时,它只会移动struct a
的大小。我说的对吗?
另一个问题是为什么pa+1
移动了32个字节而不是8个字节?指针的大小是8个字节。
@wukaihua119 是的,它应该移动到下一个(但未分配的)对象。另一个问题:std::printf( "2nd pos: %p\n", *(pa+1)
你没有打印pa+1
,你在那个位置打印地址。您应该在 a1 .. a3 分配之间进行一些分配,这样它就不会存储在连续的内存中,并且更容易发现
@wukaihua119 我添加了更明显的输出示例(-std=c++11 或者您可以展开该循环)
哦,我搞砸了。 pa+1
正是arr
的第二个元素的地址。 *(pa+1)
是a2
的地址。【参考方案2】:
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
数组中的第一个地址!所以这个计算是错误的。
【讨论】:
@Scheff 我搞砸了。你能读到第二个版本吗?以上是关于从指向结构数组的指针中提取元素的主要内容,如果未能解决你的问题,请参考以下文章