извлекать элементы из указателя на указатель на массив структур - PullRequest
0 голосов
/ 17 июня 2020

У меня проблема с указателем на массив структур.

Я объявляю и инициализирую три точки в структуре, которая имеет член 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?

Ответы [ 2 ]

1 голос
/ 17 июня 2020

Вы должны составить диаграмму, что где и как хранится:

Например:

// 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 голосов
/ 17 июня 2020

И 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! Следовательно, этот расчет неверен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...