Подтвердить что ты не робот

C указатель на двумерный массив

Я знаю, что есть несколько вопросов о том, что дает хорошие (и рабочие) решения, но ни один IMHO, который четко говорит, что это лучший способ достичь этого. Итак, предположим, что у нас есть 2D-массив:

int tab1[100][280];

Мы хотим сделать указатель, указывающий на этот 2D-массив. Для этого мы можем:

int (*pointer)[280]; // pointer creation
pointer = tab1; //assignation
pointer[5][12] = 517; // use
int myint = pointer[5][12]; // use

или, альтернативно:

int (*pointer)[100][280]; // pointer creation
pointer = &tab1; //assignation
(*pointer)[5][12] = 517; // use
int myint = (*pointer)[5][12]; // use 

Хорошо, оба, похоже, работают хорошо. Теперь я хотел бы знать:

  • Каков наилучший способ, 1-й или 2-й?
  • равны для компилятора? (скорость, перфорация...)
  • - это одно из этих решений, которое потребляет больше памяти, чем другое?
  • что чаще всего используется разработчиками?
4b9b3361

Ответ 1

//defines an array of 280 pointers (1120 or 2240 bytes)
int  *pointer1 [280];

//defines a pointer (4 or 8 bytes depending on 32/64 bits platform)
int (*pointer2)[280];      //pointer to an array of 280 integers
int (*pointer3)[100][280]; //pointer to an 2D array of 100*280 integers

Используя pointer2 или pointer3, создайте те же двоичные файлы, кроме манипуляций, как ++pointer2, как указано WhozCraig.

Я рекомендую использовать typedef (создавая тот же двоичный код, что и выше pointer3)

typedef int myType[100][280];
myType *pointer3;

Примечание: С С++ 11 вы также можете использовать ключевое слово using вместо typedef

using myType = int[100][280];
myType *pointer3;

в вашем примере:

myType *pointer;                // pointer creation
pointer = &tab1;                // assignation
(*pointer)[5][12] = 517;        // set (write)
int myint = (*pointer)[5][12];  // get (read)

Примечание. Если массив tab1 используется внутри тела функции = > , этот массив будет помещен в память стека вызовов. Но размер стека ограничен. Использование массивов, больших, чем свободный стек памяти, создает сбой при переполнении стека.

Полный фрагмент можно скомпилировать в Интернете на gcc.godbolt.org

int main()
{
    //defines an array of 280 pointers (1120 or 2240 bytes)
    int  *pointer1 [280];
    static_assert( sizeof(pointer1) == 2240, "" );

    //defines a pointer (4 or 8 bytes depending on 32/64 bits platform)
    int (*pointer2)[280];      //pointer to an array of 280 integers
    int (*pointer3)[100][280]; //pointer to an 2D array of 100*280 integers  
    static_assert( sizeof(pointer2) == 8, "" );
    static_assert( sizeof(pointer3) == 8, "" );

    // Use 'typedef' (or 'using' if you use a modern C++ compiler)
    typedef int myType[100][280];
    //using myType = int[100][280];

    int tab1[100][280];

    myType *pointer;                // pointer creation
    pointer = &tab1;                // assignation
    (*pointer)[5][12] = 517;        // set (write)
    int myint = (*pointer)[5][12];  // get (read)

    return myint;
}

Ответ 2

int *pointer[280];//Создает 280 указателей типа int.

В 32 бит os, 4 байта для каждого указателя. поэтому 4 * 280 = 1120 байт.

int (*pointer)[100][280];//Создает только один указатель, который используется для указания массива из [100] [280] ints.

Здесь всего 4 байта.

По вашему вопросу, int (*pointer)[280]; и int (*pointer)[100][280]; отличаются друг от друга, хотя он указывает на тот же 2D-массив из [100] [280].

Потому что, если int (*pointer)[280]; увеличивается, то он указывает на следующий 1D-массив, но где int (*pointer)[100][280]; пересекает весь 2D-массив и указывает на следующий байт. Доступ к этому байту может вызвать проблемы, если эта память не принадлежит вашему процессу.

Ответ 3

Оба примера эквивалентны. Однако первый из них менее очевиден и более "хакерский", в то время как второй четко указывает ваше намерение.

int (*pointer)[280];
pointer = tab1;

pointer указывает на 1D массив из 280 целых чисел. В вашем задании вы фактически назначаете первую строку tab1. Это работает, поскольку вы можете неявно использовать массивы для указателей (для первого элемента).

Когда вы используете pointer[5][12], C рассматривает pointer как массив массивов (pointer[5] имеет тип int[280]), поэтому здесь присутствует неявный листинг (по крайней мере, семантически).

В вашем втором примере вы явно создаете указатель на 2D-массив:

int (*pointer)[100][280];
pointer = &tab1;

Здесь семантика понятна: *pointer - это 2D-массив, поэтому вам нужно получить доступ к нему с помощью (*pointer)[i][j].

Оба решения используют один и тот же объем памяти (1 указатель) и, скорее всего, будут работать одинаково быстро. Под капотом оба указателя будут указывать на одну и ту же ячейку памяти (первый элемент массива tab1), и, возможно, ваш компилятор даже сгенерирует тот же код.

Первое решение является "более продвинутым", так как нужно достаточно глубокое понимание того, как массивы и указатели работают на C, чтобы понять, что происходит. Второй - более явный.