Чумовая способ распределения двумерного массива?

Обновить

November 2018

Просмотры

4.6k раз

108

В проекте, кто-то толкнул эту строку:

double (*e)[n+1] = malloc((n+1) * sizeof(*e));

Который якобы создает двумерный массив (п + 1) * (п + 1) удваивается.

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

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

3 ответы

87

Переменная eпредставляет собой указатель на массив n + 1элементов типа double.

Использование оператора разыменования на eдает основание тип из eкоторых является «массив n + 1элементов типа double».

mallocВызов просто берет базовый-тип e(пояснено выше) и получает его размер, умножает его n + 1, и передавая этот размер к mallocфункции. По существу выделение массива n + 1массивов n + 1элементов double.

56

Это типичный способ следует выделить 2D массивы динамически.

  • eэто указатель массива на массив типа double [n+1].
  • sizeof(*e)поэтому дает вид заостренного-на типа, который размер одного double [n+1]массива.
  • Вы выделить место для n+1таких массивов.
  • Вы можете установить указатель массива , eчтобы указать на первом массиве в этом массиве массивов.
  • Это позволяет использовать в eкачестве e[i][j]доступа к отдельным элементам в 2D массиве.

Лично я думаю, что этот стиль намного легче читать:

double (*e)[n+1] = malloc( sizeof(double[n+1][n+1]) );
39

Эта идиома падает естественным образом из 1D распределения массива. Давайте начнем с выделения массива 1D некоторого произвольного типа T:

T *p = malloc( sizeof *p * N );

Простой, не так ли? Выражение *p имеет тип T, поэтому sizeof *pдает тот же результат , как и sizeof (T), таким образом , мы выделить достаточно места для Nэлементного массива T. Это верно для любого типаT .

Теперь, давайте подставим Tс типом массива , как R [10]. Тогда наше распределение становится

R (*p)[10] = malloc( sizeof *p * N);

Семантика здесь точно так же , как метод выделения 1D; все , что изменилось , так это тип p. Вместо того T *, что теперь R (*)[10]. Выражение *pимеет тип , Tкоторый является типом R [10], так что sizeof *pэквивалентно sizeof (T), что эквивалентно sizeof (R [10]). Таким образом , мы выделить достаточно места для Nпо 10элементу массива R.

Мы можем взять еще дальше , если мы хотим; предположим , Rсам по себе тип массива int [5]. Запасной что Rи мы получаем

int (*p)[10][5] = malloc( sizeof *p * N);

То же самое дело - sizeof *pтакое же , как sizeof (int [10][5])и мы завершаете выделение смежной части памяти достаточно большой , чтобы провести NВ с 10помощью 5массива int.

Так что это сторона распределения; насчет стороне доступа?

Помните , что []работа нижний индекс определяется в терминах арифметики указателей: a[i]определяется как *(a + i)1 . Таким образом, оператор индексирования [] неявно разыменовывает указатель. Если pэто указатель T, вы можете получить доступ к указываемому дорожат либо явно разыменование с одноместный *оператором:

T x = *p;

или с помощью []оператора индекса:

T x = p[0]; // identical to *p

Таким образом, если pуказывает на первый элемент в массиве , вы можете получить доступ к любому элементу этого массива, используя индекс на указателе p:

T arr[N];
T *p = arr; // expression arr "decays" from type T [N] to T *
...
T x = p[i]; // access the i'th element of arr through pointer p

Теперь, давайте сделаем нашу работу заместительной снова и заменить Tс типом массива R [10]:

R arr[N][10];
R (*p)[10] = arr; // expression arr "decays" from type R [N][10] to R (*)[10]
...
R x = (*p)[i];

Непосредственно очевидное различие; мы явно разыменования pперед применением оператора индексирования. Мы не хотим , чтобы индексировать в p, мы хотим , чтобы индексировать в то , что p указывает на (в данном случае, в массиве arr[0] ). Так как унарные *имеют более низкий приоритет , чем индекс []оператор, мы должны использовать скобки для явной группы pс *. Но помните , что сверху *pтакой же , как p[0], поэтому мы можем заменить , что с

R x = (p[0])[i];

или просто

R x = p[0][i];

Таким образом, если pуказывает на 2D массива, мы можем индекс в этом массиве через pпримерно так:

R x = p[i][j]; // access the i'th element of arr through pointer p;
               // each arr[i] is a 10-element array of R

Исходя из этого к такому же выводу , как указано выше , и подставляя Rс int [5]:

int arr[N][10][5];
int (*p)[10][5]; // expression arr "decays" from type int [N][5][10] to int (*)[10][5]
...
int x = p[i][j][k];

Это работает точно так же , если pуказывает на обычный массив, или если он указывает на памяти , выделяемой через malloc.

Эта идиома имеет следующие преимущества:

  1. Это просто - одна строка кода, в отличие от метода распределения по частям
    T **arr = malloc( sizeof *arr * N );
    if ( arr )
    {
      for ( size_t i = 0; i < N; i++ )
      {
        arr[i] = malloc( sizeof *arr[i] * M );
      }
    }
    
  2. Все строки выделенного массива являются смежными * *, который не в случае с поэлементной метода распределения выше;
  3. Deallocating массив так же легко с помощью одного вызова free. Опять же , не так с методом поэлементной распределения, где вы должны освободить каждый , arr[i]прежде чем освободить arr.

Иногда метод распределения по частям, является предпочтительным, например, когда ваша куча сильно фрагментирован, и вы не можете выделить память как непрерывный кусок, или вы хотите, чтобы выделить «неровный» массив, в котором каждая строка может иметь различную длину. Но в целом, это лучший способ пойти.


1. Помните , что массивы не являются указателями - вместо массива выражения преобразуются в указатель выражения , как это необходимо.