застрял при копировании памяти, управляемой двойным указателем на двумерный массив - PullRequest
0 голосов
/ 12 октября 2019

Коды следующие:

#include <iostream>
using std::cout;

int main(int argc, char ** argv)
{
    int** p;
    int a[2][3]={{-1,-1,-1},
                 {-1,-1,-1}};
    int k=1;
    p = new int *[2];
    for(int i=0;i<2;i++) {
        p[i] = new int[3];
        for(int j=0;j<3;j++) {
            p[i][j] = k++;
        }
    }

    memcpy(a,p,2 * 3 * sizeof(int));
    for(int i=0;i<2;i++){
        for(int j=0;j<3;j++)
            cout<<a[i][j]<<" ";
        cout<<endl;
    }

    delete [] p;
    return 0;
}

Получены неожиданные результаты после запуска:

26573888 0 26573920
0   0   0

Я думаю, что это скопированные адреса, а не значения, поэтому я изменил строку memcpy(a,p,2 * 3 * sizeof(int)); на

memcpy(a,p[0],2 * 3 * sizeof(int));

И терминал распечатан:

1 2 3
0 0 0

Как понимать вышеприведенные результаты? Кто-нибудь может мне помочь?

УГАДАТЬ
адреса p[0][2] и p[1][0] не являются непрерывными?

Ответы [ 3 ]

0 голосов
/ 12 октября 2019

вам нужно сделать memcpy по одному в строке, чтобы получить ожидаемый результат:

memcpy(a[0], p[0], 3 * sizeof(int));
memcpy(a[1], p[1], 3 * sizeof(int));

Причина в том, что строки не являются смежными. *

0 голосов
/ 12 октября 2019

Какой тип p[0]?

int*

Какой тип a[0]?

int[3]

a[0] распада доуказатель в соответствующем контексте (например, при передаче функции в качестве аргумента), но он не является родным, полностью в отличие от p[0], который находится с самого начала. Вы заметите разницу, если, например, сравните размер обоих (sizeof(a[0]) дает размер трех целых, sizeof(p[0]) дает размер указателя).

Элементы массива всегда просто размещены один за другим смежно, поэтому для a есть два массива, размещенных один за другим:

a: { { -1, -1, -1 }, {-1, -1, -1 } }

Для p фактически точно то же самое, каждый член следует один за другим непрерывно;только на этот раз члены являются истинными указателями (то есть, некоторые адреса в памяти). есть других массивов, но они (обязательно) расположены в другом месте:

p: { <pointer>, <pointer> }
         |          |
         V          |
       { 1, 2, 3}   |
                    V
                  { 4, 5, 6}

Итак, в заключение, ваше предположение верно.

0 голосов
/ 12 октября 2019

Вы смешиваете вещи ...

Этот другой вопрос (и особенно принятый ответ) это хорошо объясняет. У вас есть статический двумерный массив и int**.

Эти два выглядят одинаково, но это не так. Компилятор знает, что именно, так что одно выкладывается непрерывно, а другое нет.

Вы даже можете увидеть это в своем коде. Как только вы пишете

int a[2][3]={{-1,-1,-1},
             {-1,-1,-1}};

, где объект представляет собой кусок памяти, обозначенный a

Но тогда вы пишете

p = new int *[2];
for(int i=0;i<2;i++) {
    p[i] = new int[3];
    for(int j=0;j<3;j++) {
        p[i][j] = k++;
    }
}

, где у вас есть объект p. А затем со строкой p[i] = new int[3] вы продолжаете и делаете 2 отдельных int[3] объекта. Эти врут кто знает где. И их адреса назначаются указателям p[i].

TL; DR: тип int[2][3] принципиально отличается от int**, и они не являются тривиально совместимыми.

...