Linux программирование в примерах
47 целиком в буфер. Увеличить буфер и попытаться снова. */48 if (p[-1] != '\n')49 goto more_buffer;5051 /* Мы получили новую строку, увеличить число строк. */52 ++nlines;Строки 43–52 увеличивают указатель на участок буфера за только что прочитанными данными. Затем код проверяет, является ли последний прочитанный символ символом конца строки. Конструкция
(строка 48) проверяет символ перед p, также какp[-1]является текущим символом, аp[0]— следующим. Сначала это кажется странным, но если вы переведете это на язык математики указателей,p[1], это приобретет больший смысл, а индексированная форма, возможно, проще для чтения.*(p-1)Если последний символ не был символом конца строки, это означает, что нам не хватило места, и код выходит (с помощью
) для увеличения размера буфера (строка 49). В противном случае увеличивается число строк.goto54 #if !defined(WINDOWS32) && !defined(__MSDOS__)55 /* Проверить, что строка завершилась CRLF; если так,56 игнорировать CR. */57 if ((p - start) > 1 && p[-2] == '\r')58 {59 --p;60 p[-1] = '\n';61 }62 #endifСтроки 54–62 обрабатывают вводимые строки, следующие соглашению Microsoft по завершению строк комбинацией символов возврата каретки и перевода строки (CR-LF), а не просто символом перевода строки (новой строки), который является соглашением Linux/Unix. Обратите внимание, что
исключает этот код на платформе Microsoft, очевидно, библиотека#ifdefна этих системах автоматически осуществляет это преобразование. Это верно также для других не-Unix систем, поддерживающих стандартный С.<stdio.h>64 backslash = 0;65 for (p2 = p - 2; p2 >= start; --p2)66 {67 if (*p2 != '\\')68 break;69 backslash = !backslash;70 }7172 if (!backslash)73 {74 p[-1] = '\0';75 break;76 }7778 /* Это была комбинация обратный слеш/новая строка. Если есть79 место, прочесть еще одну строку. */80 if (end - p >= 80)81 continue;8283 /* В конце буфера нужно больше места, поэтому выделить еще.84 Позаботиться о сохранении текущего смещения в p. */85 more_buffer:86 {87 unsigned long off = p - start;88 ebuf->size *= 2;89 start = ebuf->buffer=ebuf->bufstart=(char*)xrealloc(start,90 ebuf->size);91 p = start + off;92 end = start + ebuf->size;93 *p = '\0';94 }95 }До сих пор мы имели дело с механизмом получения в буфер по крайней мере одной полной строки. Следующий участок обрабатывает случай строки с продолжением. Хотя он должен гарантировать, что конечный символ обратного слеша не является частью нескольких обратных слешей в конце строки. Код проверяет, является ли общее число таких символов четным или нечетным путем простого переключения переменной
из 0 в 1 и обратно. (Строки 64–70.)backslashЕсли число четное, условие '
' (строка 72) будет истинным. В этом случае конечный символ конца строки замещается байтом NUL, и код выходит из цикла.!backshlashС другой стороны, если число нечетно, строка содержит четное число пар обратных слешей (представляющих символы \\, как в С), и конечную комбинацию символов обратного слеша и конца строки. [43] В этом случае, если в буфере остались по крайней мере 80 свободных байтов, программа продолжает чтение в цикле следующей строки (строки 78–81). (Использование магического числа 80 не очень здорово; было бы лучше определить и использовать макроподстановку.)
По достижении строки 83 программе нужно больше места в буфере. Именно здесь вступает в игру динамическое управление памятью. Обратите внимание на комментарий относительно сохранения значения
(строки 83-84); мы обсуждали это ранее в терминах повторной инициализации указателей для динамической памяти. Значение end также устанавливается повторно. Строка 89 изменяет размер памяти.pОбратите внимание, что здесь вызывается функция
. Многие программы GNU используют вместоxrealloc()иmalloc()функции-оболочки, которые автоматически выводят сообщение об ошибке и завершают программу, когда стандартные процедуры возвращаютrealloc(). Такая функция-оболочка может выглядеть таким образом:NULLextern const char *myname; /* установлено в main() */void *xrealloc(void *ptr, size_t amount) {void *p = realloc(ptr, amount);if (p == NULL) {fprintf(stderr, "%s: out of memory!\n", myname);exit(1);}return p;}Таким образом, если функция
возвращается, она гарантированно возвращает действительный указатель. (Эта стратегия соответствует принципу «проверки каждого вызова на ошибки», избегая в то же время беспорядка в коде, который происходит при таких проверках с непосредственным использованием стандартных процедур.) Вдобавок, это позволяет эффективно использовать конструкцию 'xrealloc()', против которой мы предостерегали ранее.ptr = xrealloc(ptr, new_size)