2013-02-11 4 views
24

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

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

Помните, что массив указателей (как в 'int * pointer [280];') - это не то же самое, что массив массивов. –

+0

привет, ferdinand. Я скорректировал первое решение с круглыми скобками. Извиняется. – Chrysotribax

ответ

21
//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; 

Примечание: Так как C++ 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 используется в теле функции =>, этот массив будет помещен в память стека вызовов.Но размер стека ограничен. Использование массивов, больших, чем свободный стек памяти, создает stack overflow crash.

Полный фрагмент онлайн-компилируется в 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; 
} 
+0

привет, пожалуйста, просмотрите мое первое решение: я ранее забыл скобки в создании указателя. Извиняется. – Chrysotribax

+0

@ user216993 Вы также написали '(pointer *)' вместо '(* pointer)' во второй версии. ;-) Хорошо, я обновляю свой ответ. Приветствия – olibre

+0

Спасибо, olibre за ваш четкий ответ. (и снова извиняется за опечатки ...). – Chrysotribax

9

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-массив и указывает на следующий байт. Доступ к этому байту может вызвать проблемы, если эта память не принадлежит вашему процессу.

+0

привет, пожалуйста, просмотрите мое первое решение: я ранее забыл скобки в создании указателя. Извиняется. – Chrysotribax

8

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

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. Второй - более явный.

+0

Спасибо за ваш отзыв, Фердинанд. Я согласен с вами: второе решение кажется более явным. Я буду продолжать использовать этот. – Chrysotribax

Смежные вопросы