Объяснение кода динамического распределения памяти - PullRequest
4 голосов
/ 17 июня 2011

Будучи начинающим программистом на C / C ++, мне пришлось потратить несколько часов, пытаясь расшифровать код, приведенный ниже: Может ли кто-нибудь пройтись со мной (пошагово через код ниже для динамического распределения памяти) построчно.

 char **alloc_2d_char(const int rows, const int cols) 
 {
   char *data = (char *)malloc(rows*cols*sizeof(char));
   char **array= (char **)malloc(rows*sizeof(char*));

   for (int i=0; i<rows; i++)
      array[i] = &(data[cols*i]);

   return array;
 }

Указатель на указатели объясняется отдельно, чем от указателей на массивы.Я был в состоянии получить части информации из различных источников, но ни один, который сшивает линии согласованно.

Ответы [ 5 ]

5 голосов
/ 17 июня 2011

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

char *data = (char *)malloc(rows*cols*sizeof(char));

ОК - эта строка выделяет пространство для всего двумерного массива. 2-D массив - это rows строк по cols столбцам. Таким образом, общее количество элементов составляет rows * cols. Затем вы должны умножить это на количество места, занимаемого каждым элементом, которое составляет sizeof(char), так как это двумерный массив char. Таким образом, общий объем выделяемой памяти равен rows * cols * sizeof(char), что действительно является аргументом для malloc.

Вызов malloc возвращает указатель на выделенную память. Поскольку эта память будет использоваться для хранения char, вы возвращаете возвращаемое значение к char *.

char **array= (char **)malloc(rows*sizeof(char*));

array объявляется как тип "указатель на указатель на символ", потому что это то, что он собирается делать. Это укажет на память, которая будет держать указатели на символ. Это будет один указатель для каждой строки. Таким образом, вы должны выделить rows * sizeof(char *) памяти: количество указателей, умноженное на размер указателя правильного типа. И так как это было выделено, чтобы указывать на указатели на char, мы возвращаем значение возврата к char **.

for (int i=0; i<rows; i++)
   array[i] = &(data[cols*i]);

Это магия :). Это устанавливает каждый указатель в array так, чтобы он указывал на в блоке фактических данных, выделенных ранее. Рассмотрим конкретный пример, где rows равно 2, а cols равно 3. Тогда в памяти есть блок из 6 символов:

 [0][1][2][3][4][5]

И data[n] (для n от 0 до 5) - это n-й элемент, а &data[n] - это * адрес для n-го элемента.

Итак, что делает этот цикл в этом случае:

array[0] = &data[0];
array[1] = &data[3];

Таким образом, array[0] указывает на подблок, начинающийся с [0], и array[1] указывает на подблок, начинающийся с [3]. Затем, когда вы добавляете второй индекс, вы индексируете с начала этого указателя. Таким образом, array[0][2] означает «получить указатель, сохраненный в array[0]. Найдите то, на что он указывает, а затем продвиньтесь на 2 элемента оттуда.»

array[0] указывает на [0][1][2] (ну, фактически указывает на [0]). Затем вы продвигаете два элемента вперед и получаете [2].

Или, если вы начинаете с array[1][1], array[1] указывает на [3][4][5] (и фактически указывает на [3]. Переместите один элемент вперед и получите [4].

1 голос
/ 17 июня 2011

Каждый * в объявлении относится к одному уровню косвенности указателя. поэтому int ** означает указатель на указатель на int. Итак, ваша функция:

char **alloc_2d_char(const int rows, const int cols) 
{

возвращает указатель на указатель на символ.

  char *data = (char *)malloc(rows*cols*sizeof(char));

Здесь указывается указатель на char. Указатель называется data. Инициализация вызывает malloc, который выделяет количество байтов, равное значению аргумента. Это означает, что есть rows*cols*sizeof(char) байтов, которые будут равны rows*cols, поскольку char равен 1 байту. Функция malloc возвращает указатель на новую память , что означает, что data теперь указывает на кусок памяти, который rows*cols большой. (char *) перед вызовом malloc просто приводит новую память к тому же типу, что и указатель data.

  char **array= (char **)malloc(rows*sizeof(char*));

array - указатель на указатель на char. Он также присваивается с помощью malloc. Объем выделяемой памяти на этот раз равен rows*sizeof(char), что равно rows. Это означает, что array является указателем на указатель на кусок памяти, достаточно большой, чтобы вместить 1 строку.

 for (int i=0; i<rows; i++)
    array[i] = &(data[cols*i]);

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

col:   0 1 2 3 ... cols-1
row: 0 *
     1 *
     2 *
     3 *
     .
     .
     .
rows-1 *

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

   return array;
 }

Это просто возвращает ваш array указатель на указатель, который соответствует типу возврата функции alloc_2d_char: char **. Это означает, что вызывающая функция по существу получит массив указателей, и каждый из этих указателей указывает на одну из строк двумерного массива.

1 голос
/ 17 июня 2011

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

Ниже приведен исходный код с добавленными комментариями, чтобы попытаться описать каждый шаг:

char **alloc_2d_char(const int rows, const int cols) 
{
  // This allocates the chunk of memory that stores that actual data.
  // It is a one single-dimensional array that has rows*cols characters.
  char *data = (char *)malloc(rows*cols*sizeof(char));

  // This allocates an array of pointers that will then be assigned to the
  // individual rows (carved out of the previous allocation).
  char **array= (char **)malloc(rows*sizeof(char*));

  // This assigns each row to the appropriate position in the data array.  
  // The &(data[cols*i]) bit of it is the address of a portion in the 
  // memory pointed to by data.  The cols*i is the offset to the portion that
  // represents row i.
  for (int i=0; i<rows; i++)
     array[i] = &(data[cols*i]);

  // After doing this, then you can assign a value such as:
  //   array[r][c] = 'x';
  // That statement would store 'x' into data[r*c + c]

  return array;
}
1 голос
/ 17 июня 2011

Не сложно расшифровать ...

char *data = (char *)malloc(rows*cols*sizeof(char));

простое выделение памяти

char **array= (char **)malloc(rows*sizeof(char*));

выделение памяти #row указателей на символы

array[i] = &(data[cols*i]);

каждый массив[i] - указатель, указатель на данные [cols * i]

1 голос
/ 17 июня 2011

Первый malloc получает память для двумерного массива символов. Второй malloc получает память для индекса строки.

Цикл for устанавливает указатель на каждую строку.

Наконец возвращается индекс строки.

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