netlib.narod.ru< Назад | Оглавление | Далее >

7.3. Списки аргументов переменной длины

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

Объявление стандартной функции printf выглядит так:

  int printf(char *fmt, ...)

Многоточие в объявлении означает, что число и типы аргументов могут изменяться. Знак многоточия может стоять только в конце списка аргументов. Наша функция minprintf объявляется как

  void minprintf(char *fmt, ...)

поскольку она не будет возвращать число выводимых символов, как это делает printf.

Вся сложность в том, каким образом minprintf будет продвигаться вдоль списка аргументов, — ведь у этого списка нет даже имени. Стандартный заголовочный файл <stdarg.h> содержит набор макроопределений, которые устанавливают, как шагать по списку аргументов. Содержание этого заголовочного файла может изменяться от машины к машине, но представленный им интерфейс везде одинаков.

Тип va_list служит для описания переменной, которая будет по очереди указывать на каждый из аргументов; в minprintf эта переменная имеет имя ap (от «argument pointer» — указатель на аргумент). Макрос va_start инициализирует переменную ap, чтобы она указывала на первый безымянный аргумент. К va_start нужно обратиться до первого использования ap. Среди аргументов по крайней мере один должен быть именованным; при начальной установке макроопределение использует в качестве отправной точки последний именованный аргумент.

Макрос va_arg при каждом вызове возвращает очередной аргумент, и предвигает указатель ap на следующий; по передаваемому ему имени типа он определяет тип возвращаемого значения и размер шага для перехода к следующему аргументу. Наконец, макрос va_end делает очистку всего, что необходимо. К va_end следует обратиться перед самым выходом из функции.

Перечисленные средства образуют основу нашей упрощенной версии printf.

  #include <stdarg.h>

  /* minprintf: сокращенная верся функции printf
     с переменным числом аргументов */
  void minprintf(char *fmt, ...)
  {
      va_list ap; /* указывает на безымянные аргументы */
      char *p, *sval;
      int ival;
      double dval;

      va_start(ap, fmt); /* устанавливает ap на 1 безымянный аргумент */
      for (p =fmt; *p; p++) {
          if (*p != '%') {
              putchar(*p);
              continue;
          }
          switch (*++p) {
          case 'd':
              ival = va_arg(ap, int);
              printf("%d", ival);
              break;
          case 'f':
              dval = va_arg(ap, double);
              printf("%f", dval);
              break;
          case 's':
              for (sval = va_arg(ap, char *); *sval; sval++)
                  putchar(*sval);
              break;
          default:
              putchar(*p);
              break;
          }
      }
      va_end(ap); /* очистка, когда все сделано */
  }

Упражнение 7-3


Дополните minprintf другими возможностями printf.



netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz