Категории
Самые читаемые
PochitayKnigi » Компьютеры и Интернет » Программное обеспечение » Linux программирование в примерах - Арнольд Роббинс

Linux программирование в примерах - Арнольд Роббинс

Читать онлайн Linux программирование в примерах - Арнольд Роббинс

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 16 17 18 19 20 21 22 23 24 ... 165
Перейти на страницу:

#define getnode(n) if (nextfree) n = nextfree,

 nextfree = nextfree->nextp; else n = more_nodes()

#define freenode(n) ((n)->flags = 0, (n)->exec_count = 0,

 (n)->nextp = nextfree, nextfree = (n))

Переменная nextfree указывает на связанный список структур NODE. Макрос getnode() убирает из списка первую структуру, если она там есть. В противном случае она вызывает more_nodes(), чтобы выделить новый список свободных структур NODE. Макрос freenode() освобождает структуру NODE, помещая его в начало списка.

ЗАМЕЧАНИЕ. Первоначально при написании своего приложения делайте это простым способом: непосредственно используйте malloc() и free(). Написание собственного распределителя вы должны рассмотреть лишь в том и только в том случае, если профилирование вашей программы покажет, что она значительную часть времени проводит в функциях выделения памяти.

3.2.1.8. Пример: чтение строк произвольной длины

Поскольку это, в конце концов, Программирование на Linux в примерах, настало время для примера из реальной жизни. Следующий код является функцией readline() из GNU Make 3.80 (ftp://ftp.gnu.org/gnu/make/make-3.80.tar.gz). Ее можно найти в файле read.c.

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

Вторичной задачей является распоряжение продлением строк. Как и в С, строки, заканчивающиеся обратным слешем, логически продолжаются со следующей строки. Используется стратегия поддержания буфера. В нем хранится столько строк, сколько помещается в буфер, причем указатели отслеживают начало буфера, текущую строку и следующую строку. Вот структура:

struct ebuffer {

 char *buffer;      /* Начало текущей строки в буфере. */

 char *bufnext;     /* Начало следующей строки в буфере. */

 char *bufstart;    /* Начало всего буфера. */

 unsigned int size; /* Размер буфера для malloc. */

 FILE *fp;          /* Файл или NULL, если это внутренний буфер. */

 struct floc floc;  /* Информация о файле в fp (если он есть). */

};

Поле size отслеживает размер всего буфера, a fp является указателем типа FILE для файла ввода. Структура floc не представляет интереса при изучении процедуры.

Функция возвращает число строк в буфере. (Номера строк здесь даны относительно начала функции, а не исходного файла.)

1  static long

2  readline(ebuf) /* static long readline(struct ebuffer *ebuf) */

3  struct ebuffer *ebuf;

4  {

5   char *p;

6   char *end;

7   char *start;

8   long nlines = 0;

9

10  /* Использование строковых буферов и буферов потоков достаточно

11     различается, чтобы использовать разные функции. */

12

13  if (!ebuf->fp)

14   return readstring(ebuf);

15

16  /* При чтении из файла для каждой новой строки мы всегда

17     начинаем с начала буфера. */

18

19  p = start = ebuf->bufstart;

20  end = p + ebuf->size;

21  *p = '';

Для начала заметим, что GNU Make написан на С K&R для максимальной переносимости. В исходной части объявляются переменные, и если ввод осуществляется из строки (как в случае расширения макроса), код вызывает другую функцию, readstring() (строки 13 и 14). Строка '!ebuf->fp' (строка 13) является более короткой (и менее понятной, по нашему мнению) проверкой на пустой указатель; это то же самое, что и 'ebuf->fp==NULL'.

Строки 19-21 инициализируют указатели и вводят байт NUL, который является символом завершения строки С в конце буфера. Затем функция входит в цикл (строки 23–95), который продолжается до завершения всего ввода.

23 while (fgets(p, end - р, ebuf->fp) != 0)

24 {

25  char *p2;

26  unsigned long len;

27  int backslash;

28

29  len = strlen(p);

30  if (len == 0)

31  {

32   /* Это случается лишь тогда, когда первый символ строки ''.

33      Это довольно безнадежный случай, но (верите или нет) ляп Афины

34      бьет снова! (xmkmf помещает NUL в свои makefile.)

35      Здесь на самом деле нечего делать; мы создаем новую строку, чтобы

36      следующая строка не была частью данной строки. */

37   error (&ebuf->floc,

38    _("warning: NUL character seen; rest of line ignored"));

39   p[0] = 'n';

40   len = l;

41  }

Функция fgets() (строка 23) принимает указатель на буфер, количество байтов для прочтения и переменную FILE* для файла, из которого осуществляется чтение. Она читает на один байт меньше указанного, чтобы можно было завершить буфер символом ''. Эта функция подходит, поскольку она позволяет избежать переполнения буфера. Она прекращает чтение, когда встречается с символами конца строки или конца файла; если это символ новой строки, он помещается в буфер. Функция возвращает NULL при неудаче или значение указателя первого аргумента при успешном завершении.

В этом случае аргументами являются указатель на свободную область буфера, размер оставшейся части буфера и указатель FILE для чтения.

Комментарии в строках 32–36 очевидны; если встречается нулевой байт, программа выводит сообщение об ошибке и представляет вывод как пустую строку. После компенсирования нулевого байта (строки 30–41) код продолжает работу.

43 /* Обойти только что прочитанный текст. */

44 p += len;

45

46 /* Если последний символ - не конец строки, она не поместилась

47    целиком в буфер. Увеличить буфер и попытаться снова. */

48 if (p[-1] != 'n')

49  goto more_buffer;

50

51 /* Мы получили новую строку, увеличить число строк. */

52 ++nlines;

Строки 43–52 увеличивают указатель на участок буфера за только что прочитанными данными. Затем код проверяет, является ли последний прочитанный символ символом конца строки. Конструкция p[-1] (строка 48) проверяет символ перед p, также как p[0] является текущим символом, а p[1] — следующим. Сначала это кажется странным, но если вы переведете это на язык математики указателей, *(p-1), это приобретет больший смысл, а индексированная форма, возможно, проще для чтения.

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

54 #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. Обратите внимание, что #ifdef исключает этот код на платформе Microsoft, очевидно, библиотека <stdio.h> на этих системах автоматически осуществляет это преобразование. Это верно также для других не-Unix систем, поддерживающих стандартный С.

64  backslash = 0;

65  for (p2 = p - 2; p2 >= start; --p2)

66  {

67   if (*p2 != '\')

68   break;

69   backslash = !backslash;

70  }

71

72  if (!backslash)

73  {

74   p[-1] = '';

75   break;

76  }

77

78  /* Это была комбинация обратный слеш/новая строка. Если есть

79     место, прочесть еще одну строку. */

80  if (end - p >= 80)

81   continue;

82

83  /* В конце буфера нужно больше места, поэтому выделить еще.

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 = '';

94  }

95 }

До сих пор мы имели дело с механизмом получения в буфер по крайней мере одной полной строки. Следующий участок обрабатывает случай строки с продолжением. Хотя он должен гарантировать, что конечный символ обратного слеша не является частью нескольких обратных слешей в конце строки. Код проверяет, является ли общее число таких символов четным или нечетным путем простого переключения переменной backslash из 0 в 1 и обратно. (Строки 64–70.)

1 ... 16 17 18 19 20 21 22 23 24 ... 165
Перейти на страницу:
Тут вы можете бесплатно читать книгу Linux программирование в примерах - Арнольд Роббинс.
Комментарии