从指向结构数组的指针中提取元素。

问题描述 投票:0回答:1

我有一个问题,一个点到结构数组的指针。

我声明并初始化了三个指向结构体的指针,这个结构体有一个成员。val. 我创建了一个指针 arr 到一个指向结构指针的数组。然后我创建了一个指针 pa 指针 arr 和一个指针 pa1pa.

当我试图提取第二个元素的 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 valpa1 不是 3 不过 0?

都有 papa1 似乎指向同一个地址 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 pospapa1 其实是不一样的。

现在的问题是 如何从 arr 使用 pa1?

c++ pointers struct
1个回答
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

顺便说一下,你可以用调试器和变量表来跟踪发生了什么,什么东西指向哪里,以及它的值。

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

0
投票

都是 papa1 似乎指向同一个地址 0000000000e761e4.

但是... 它们是不同类型的。因此 +1 给出了一个不同的地址。

pa1 属于 a*因此 pa1+1reinterpret_cast<a*>(reinterpret_cast<unsigned char*>(pa1)+sizeof(a)). 请注意,我们去掉了一层计算偏移量的间接性。

pa 的类型是 a**因此 pa1+1reinterpret_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! 因此,这个计算是错误的。

© www.soinside.com 2019 - 2024. All rights reserved.