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

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

Ответы на вопросы разбиты по темам:

1. Нулевые указатели

2. Указатели и массивы.

3. Выделение памяти

4. Выражения

5. ANSI C

6. Препроцессор С.

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

8. Булевы выражения и переменные.

9. Структуры, перечисления и объединения.

10. Декларации.

11. Cтандартный ввод/вывод.

12. Библиотечные функции.

13. Lint.

14. Стиль.

15. Операции с плавающей точкой.

16. Интерфейс с операционной системой.

17. Разное (Пребразование Fortran -> C , грамматики для YACC и т.п.)

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

7.1    Как реализовать    функцию    с переменным числом аргументов?

О:    Используйте головной файл <stdarg.h> (или, если    необходимо, более старый <varargs.h>).
Вот пример функции, которая объединяет произвольное количество стрингов, помещая результат в выделенный с помощью malloc участок памяти.
#include <stdlib.h>           /* для malloc, NULL, size_t */ #include <stdarg.h>           /* для va_ макросов */ #include <string.h>           /* для strcat и т.п. */
char *vstrcat(char *first, …) { size_t len = 0; char *retbuf; va_list argp; char *p; if(first == NULL) return NULL; len =    strlen(first); va_start(argp, first); while((p = va_arg(argp, char *)) != NULL) len += strlen(p); va_end(argp); retbuf = malloc(len +    1);    /* +1 для \0 */ if(retbuf == NULL) return NULL;          /* ошибка */ (void)strcpy(retbuf, first); va_start(argp, first); while((p = va_arg(argp, char *)) != NULL) (void)strcat(retbuf, p); va_end(argp); return retbuf; }
Вызывается функция примерно так:
char *str = vstrcat(«Hello, «, «world!»,    (char *)NULL);
Обратите внимание на явное приведение типа в последнем аргументе. (Помните также, что после вызова такой функции нужно освободить память).
Если компилятор разрабатывался до приема стандарта ANSI, перепишите определение функции без    прототипа («char *vstrcat(first) char *first; {«) включите <stdio.h> вместо <stdlib.h>, добавьте
«extern char *malloc();» ,
и используйте int вместо size_t. Возможно, придется удалить приведение (void) и использовать varargs.h вместо stdarg. Дополнительные соображения смотрите в следующем вопросе.
Помните, что в прототипах функций со списком аргументов    переменной длины не указывается тип аргументов. Это значит, что по    умолчанию будет происходить «расширение» типов аргументов    (см. вопрос 5.8). Это также значит, что тип нулевого указателя должен быть явно указан (см. вопрос 1.2).
Смотри: K&R II Разд. 7.3 c. 155, Разд. B7 c. 254; H&S Разд. 13.4 c. 286-9; ANSI Разд 4.8 по 4.8.1.3 .

7.2    Как написать функцию, которая бы, подобно printf, получала строку формата и переменное число аргументов, а затем для выполнения большей части работы передавала бы все это printf?

О:    Используйте vprintf, vfprintf, или vsprintf.
Перед Вами подпрограмма «error», которая после строки «error: » печатает сообщение об ошибке и символ новой строки.
#include <stdio.h> #include <stdarg.h>
void error(char *fmt, …) { va_list argp; fprintf(stderr, «error: «); va_start(argp, fmt); vfprintf(stderr, fmt,    argp); va_end(argp); fprintf(stderr, «\n»); }
Чтобы использовать старый головной файл    <varargs.h> вместо <stdarg.h>, измените заголовок функции
void error(va_alist) va_dcl { char *fmt;
измените строку    с va_start
va_start(argp);
и добавьте строку
fmt =    va_arg(argp, char *);
между вызовами va_start    и vfprintf. Заметьте, что после    va_dcl нет точки с    запятой.
Смотри: K&R II Разд. 8.3 c. 174, Разд. B1.2 c. 245; H&S Разд. 17.12 c. 337; ANSI Разд. 4.9.6.7, 4.9.6.8, 4.9.6.9 .

7.3    Как определить,    сколько    аргументов передано функции?

О:      Для переносимых программ такая информация недоступна. Некоторые старые системы имели нестандартную функцию nargs(), но ее полезность всегда была сомнительна, поскольку обычно эта функция возвращает количество передаваемых машинных слов, а не число аргументов. (Структуры и числа с плавающей точкой обычно передаются в нескольких словах).
Любая функция с    переменным числом аргументов должна быть способна по самим аргументам определить их число. Функции типа printf определяют число аргументов по спецификаторам формата (%d и т.п.) в строке формата (вот почему эти функции так скверно ведут себя при несовпадении списка аргументов и строки формата). Другой общепринятый прием — использовать признак конца списка (часто это числа 0,-1, или нулевой указатель, приведенный к нужному типу). Смотри примеры функций execl и vstrcat (вопросы 1.2 и 7.1).

7.4    Мне не удается добиться    того, чтобы макрос va_arg возвращал аргумент типа указатель-на-функцию.

О:      Манипуляции с переписыванием типов, которыми обычно занимается va_arg, кончаются неудачей, если тип аргумента слишком сложен — вроде указателя на функцию. Если, однако, использовать typedef для определния указателя на функцию, то все будет нормально.
Смотри: ANSI Разд. 4.8.1.2 c. 124.

7.5    Как написать функцию с переменным числом аргументов, которая передает их другой функции с переменным числом аргументов?

О:    В общем    случае задача неразрешима. В качестве второй функции нужно использовать такую, которая принимает указатель типа va_list, как это делает vfprintf в приведенном выше примере. Если аргументы должны передаваться непосредственно (a не через указатель типа va_list), и вторая функция принимает переменное число аргументов (и нет возможности создать альтернативную функцию, принимающую указатель va_list), то создание переносимой программы невозможно. Проблема может быть решена, если обратиться к языку ассемблера соответствующей машины.

7.6     Как вызвать функцию со списком аргументов, создаваемым в процессе выполнения?

О:    Не существует способа, который бы гарантировал переносимость. Если у Вас пытливый ум, раздобудьте редактор    таких списков, в нем есть несколько безрассудных идей, которые можно    попробовать… (См. также вопрос 16.11).

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *