Почему указатель на указатель (int ** a)? - PullRequest
6 голосов
/ 01 февраля 2010
int **a = malloc2d(M, N) // dynamically allocates a 2D array

Какова цель наличия int ** a Vice int * a. Я понимаю, что указатели необходимы для динамического размещения, но зачем указатель на указатель?

Для 3-мерного массива это будет:

int ***a

Ответы [ 7 ]

8 голосов
/ 01 февраля 2010

Вам нужен указатель на указатель по нескольким причинам.

В приведенном вами примере двойной указатель используется для хранения двумерного массива.

2D массив в C рассматривается как одномерный массив, элементы которого 1D массивы (строки).

Например, массив T размером 4x3 (где «T» - некоторый тип данных) может быть объявлено: "T mat [4] [3]", и описывается следующим схема:

                       +-----+-----+-----+
  mat == mat[0]   ---> | a00 | a01 | a02 |
                       +-----+-----+-----+
                       +-----+-----+-----+
         mat[1]   ---> | a10 | a11 | a12 |
                       +-----+-----+-----+
                       +-----+-----+-----+
         mat[2]   ---> | a20 | a21 | a22 |
                       +-----+-----+-----+
                       +-----+-----+-----+
         mat[3]   ---> | a30 | a31 | a32 |
                       +-----+-----+-----+

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

void make_foofoo(int** change) {
    *change = malloc(4);
}
7 голосов
/ 01 февраля 2010

В C указатель на тип T указывает на место, где хранятся некоторые данные типа T.Чтобы конкретизировать, я расскажу о T = int ниже.

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

int a = 42;
int *pa = &a;

Now, *pa иa одинаковы и равны 42.Кроме того, *pa и pa[0] оба эквивалентны: так, например, вы можете сделать:

*pa += 1; /* a is 43 */
pa[0] += 1; /* a is 44 */
a += 1; /* a is 45 */

Фактически, компилятор C автоматически переводит pa[0] в *(pa+0).

Указатель может указывать на местоположение, которое находится внутри последовательности данных:

int arr[] = { 1, 2, 3 }; /* 3 ints */
int *parr = arr; /* points to 1 */

Теперь наша память выглядит так:

            +---+---+---+
       arr: | 1 | 2 | 3 |
            +---+---+---+
 +------+     |
 | parr | ----+
 +------+

parr - указательэто указывает на первый элемент arr на картинке выше.Между прочим, parr также имеет рамку вокруг него, потому что нам нужно хранить объект parr где-то в памяти.Значение parr является адресом первого элемента arr.

Теперь мы можем использовать parr для доступа к элементам arr:

arr[0] == parr[0]; /* true */
parr[1]++; /* make arr[1] equal to 3 */

Таким образом, указатель может использоваться для обозначения: «Я указываю на первый из n элементов в непрерывном хранилище некоторых объектов».Конечно, нужно знать, сколько объектов существует для этой схемы, но как только мы не забудем это сделать, это чрезвычайно удобный способ доступа к памяти в C.

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

#include <stdlib.h>
size_t n;
/* now, obtain a value in n at runtime */
int *p = malloc(n * sizeof *p);

Если вызов malloc() завершается успешно, p теперь указывает на первую из смежных областей, выделенных на 10 int с.Теперь мы можем использовать в наших программах от p[0] до p[n-1].

Вы, вероятно, знали большинство или все вышеперечисленное :-), но все же вышеизложенное помогает понять, что я скажу дальше.

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

#include <stdlib.h>
int **pp;
pp = malloc(3 * sizeof *pp);

Теперь pp указывает на int *.Возвращаясь к нашей более ранней картинке:

            +------+------+------+
            |      |      |      |
            +------+------+------+
 +------+     |
 |  pp  | ----+
 +------+

И каждый из 3 блоков - это int *, который может указывать на первый элемент непрерывной последовательности int s:

for (i=0; i < 3; ++i)
    pp[i] = malloc((i + 1) * sizeof *ppi[i]);

Здесь мы выделили пространство для одного целого в pp[0], 2 в pp[1] и 3 в pp[3]:

             +------+         +---+
 pp -------->|      |-------->|   |
             +------+         +---+---+
             |      |-------->|   |   |
             +------+         +---+---+---+
             |      |-------->|   |   |   |
             +------+         +---+---+---+

Итак, pp[0] - указатель на один int, и int является единственным int в динамически распределенном блоке int с.Другими словами, pp[0][0] является int и указывает на самое верхнее поле «width-3» выше.Аналогичным образом, pp[1][0] и pp[1][1] являются действительными и представляют собой два поля под полем pp[0][0].

Наиболее распространенное использование указателя на указатель - это создание двумерного «массива» во время выполнения.:

int **data;
size_t i;
data = malloc(n * sizeof *data);
for (i=0; i < n; ++i)
    data[i] = malloc(m * sizeof *data[i]);

Теперь, при условии, что все malloc() s выполнены успешно, data[0] ... data[n-1] являются действительными int * значениями, каждое из которых указывает на отдельную длину m непрерывно intobjects.

Но, как я показал выше, указатель на указатель не обязательно должен иметь одинаковое количество элементов в каждой из своих «строк».Наиболее очевидный пример - argv в main().

К настоящему времени, как вы можете догадаться, указатель "3-уровневый глубокий", такой как int ***p;, в порядке, иможет быть полезен в C.

2 голосов
/ 01 февраля 2010

Просто для удовольствия, вот пример *****:

  1. у нас есть 3d текстура (объемная текстура) - 3 измерения, следовательно ***
  2. каждый воксель в текстуре имеет цвет (*)
  3. Текстура анимированная, имеет количество кадров во времени (*)

Следовательно, у нас будет tex [2] [3] [10] [2] [10] .... что:

  1. Координата 2,3,10 в 3D
  2. цвет 2 (зеленый)
  3. кадр 10

Чтобы передать такие данные с возможностью их изменения, вам нужно передать int****** ... веселье! XD

(и именно поэтому мне нравятся структуры и классы ....)

1 голос
/ 01 февраля 2010

Обычный массив C - это указатель на блок памяти.

2D-массив - это указатель на список указателей на каждую строку массива.

итак, указатель на указатель.

1 голос
/ 01 февраля 2010

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

1 голос
/ 01 февраля 2010

Помогает нарисовать картинку. Представьте себе одномерный массив, где каждый элемент содержит указатель. Каждый из этих указателей указывает на другой одномерный массив. malloc2d не является стандартной библиотечной функцией, но я предполагаю, что она возвращает двумерный массив, построенный таким образом.

0 голосов
/ 01 февраля 2010

вы когда-либо передавали размер указателя (чаще всего 32 или 64 бита), а не целые объекты.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...