Понимание примера кода C с выделением памяти - PullRequest
3 голосов
/ 28 ноября 2011

Я новичок в C и читаю "Язык программирования C" от K & R, чтобы выучить его. У меня возник вопрос об этой функции, которая появилась на стр. 109 2-го издания:

/* readlines:  read input lines */
int readlines(char *lineptr[], int maxlines)
{
   int len, nlines;
   char *p, line[MAXLEN];
   nlines = 0;
   while ((len = getline(line, MAXLEN)) > 0)
       if (nlines >= maxlines || p = alloc(len) == NULL)
           return -1;
       else {
           line[len-1] = '\0';  /* delete newline */
           strcpy(p, line);
           lineptr[nlines++] = p;
       }
   return nlines;
}

Мне было интересно, почему *p вообще необходим здесь? p выделяется память, а затем line копируется в нее. Почему нельзя использовать line, поэтому в конце lineptr[nlines++] = p можно заменить на lineptr[nlines++] = line.

Ответы [ 6 ]

1 голос
/ 28 ноября 2011

Назначение вида lineptr[nlines++] = p в c / c ++ устанавливает адрес lineptr[nlines++] в адрес, на который указывает p, данные здесь не копируются.

так, адрес line всегда один и тот же адрес; поэтому lineptr[nlines++] = line означает, что все lineptr[i] будут указывать на один и тот же адрес. худшая часть, после возврата функции, line больше не существует, поэтому каждый lineptr[i] указывает на какой-то неверный адрес.

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

1 голос
/ 28 ноября 2011

Новый фрагмент памяти должен быть выделен для каждой строки.Указатель p - единственный дескриптор, который у нас есть для этой памяти.Мы присваиваем lineptr[nlines++] = p, чтобы мы могли ссылаться на каждый кусок памяти (например, строку) как часть массива lineptr.

1 голос
/ 28 ноября 2011

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

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

1 голос
/ 28 ноября 2011
lineptr[nlines++] = line;

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

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

1 голос
/ 28 ноября 2011

Если вы не выделите память для каждой строки, вы получите lineptr, представляющий собой массив, полный указателей только на последнюю прочитанную строку (не говоря уже о стековой памяти, которая, вероятно, будет перезаписана),Выделение памяти для каждой строки во время чтения имеет смысл для возвращаемого массива.В качестве примера, скажем, что line происходит в стеке по адресу 0x1000.Если вы сделаете предлагаемое изменение, результирующий массив lineptr для файла из 8 строк будет выглядеть следующим образом:

0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000

Yowch!Выделение памяти для каждой строки при ее чтении, а затем копирование строки в эту выделенную память - единственное решение.

0 голосов
/ 28 ноября 2011

Хорошо, давайте покажем вам, как сделать то же самое без char *p ... Я собираюсь немного изменить код.

/* readlines:  read input lines */

#include <string.h>

/* put that include line below #include <stdio.h> if you don't already have this
   string.h defines strdup() function which we use below */


int readlines(char *lineptr[], int maxlines)
{
   int len, nlines;
   char line[MAXLEN];
   nlines = 0;

   while ((len = getline(line, MAXLEN)) > 0) {
           line[len-1] = '\0';  /* delete newline */
           lineptr[nlines] = strdup(line); /* allocate memory and make a copy */
           if (lineptr[nlines] == NULL) {
                  return -1;
           }

           nlines++;

           if (nlines >= marlines) 
               break;


   }

   return nlines;
}

Этот код является ближайшим без временного char *pиспользовать.

Дело в том, что, хотя это функционально правильно, использование временной переменной char *p для проверки всех распределений и извлечения делает код более понятным для чтения, легким для понимания в учебных целях.Он также явно показывает выделение памяти строк как отдельный шаг, который скрыт в strdup.

...