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-й?
- равны для компилятора? (скорость, перфорация...)
- - это одно из этих решений, которое потребляет больше памяти, чем другое?
- что чаще всего используется разработчиками?
Ответы
Ответ 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, чтобы понять, что происходит. Второй - более явный.