ся программа, так что ARGC по меньшей мере равен 1. В приве-
денном выше примере ARGC равен 3, а ARGV[0], ARGV[1] и
ARGV[2] равны соответственно "ECHO", "HELLO," и "WORLD".
Первым фактическим агументом является ARGV[1], а последним -
ARGV[ARGC-1]. Если ARGC равен 1, то за именем программы не
следует никакой командной строки аргументов. Все это показа-
но в ECHO:
MAIN(ARGC, ARGV) /* ECHO ARGUMENTS; 1ST VERSION */
INT ARGC;
CHAR *ARGV[];
\(
INT I;
FOR (I = 1; I < ARGC; I++)
PRINTF("%S%C", ARGV[I], (I 0)
PRINTF("%S%C",*++ARGV, (ARGC > 1) ? ' ' : '\N');
\)
Так как ARGV является указателем на начало массива строк-ар-
гументов, то, увеличив его на 1 (++ARGV), мы вынуждаем его
указывать на подлинный аргумент ARGV[1], а не на ARGV[0].
Каждое последующее увеличение передвигает его на следующий
аргумент; при этом *ARGV становится указателем на этот аргу-
мент. одновременно величина ARGC уменьшается; когда она об-
ратится в нуль, все аргументы будут уже напечатаны.
Другой вариант:
MAIN(ARGC, ARGV) /* ECHO ARGUMENTS; 3RD VERSION */
INT ARGC;
CHAR *ARGV[];
\(
WHILE (--ARGC > 0)
PRINTF((ARGC > 1) ? "%S" : "%S\N", *++ARGV);
\)
Эта версия показывает, что аргумент формата функции PRINTF
может быть выражением, точно так же, как и любой другой. Та-
кое использование встречается не очень часто, но его все же
стоит запомнить.
Как второй пример, давайте внесем некоторые усовершенст-
вования в программу отыскания заданной комбинации символов
из главы 4. Если вы помните, мы поместили искомую комбинацию
глубоко внутрь программы, что очевидно является совершенно
неудовлетворительным. Следуя утилите GREP системы UNIX, да-
вайте изменим программу так, чтобы эта комбинация указыва-
лась в качестве первого аргумента строки.
#DEFINE MAXLINE 1000
MAIN(ARGC, ARGV) /* FIND PATTERN FROM FIRST ARGUMENT */
INT ARGC;
CHAR *ARGV[];
\(
CHAR LINE[MAXLINE];
IF (ARGC != 2)
PRINTF ("USAGE: FIND PATTERN\N");
ELSE
WHILE (GETLINE(LINE, MAXLINE) > 0)
IF (INDEX(LINE, ARGV[1] >= 0)
PRINTF("%S", LINE);
\)
Теперь может быть развита основная модель, иллюстрирую-
щая дальнейшее использование указателей. Предположим, что
нам надо предусмотреть два необязательных аргумента. Один
утверждает: "напечатать все строки за исключением тех, кото-
рые содержат данную комбинацию", второй гласит: "перед каж-
дой выводимой строкой должен печататься ее номер".
Общепринятым соглашением в "с"-программах является то,
что аргумент, начинающийся со знака минус, вводит необяза-
тельный признак или параметр. Если мы, для того, чтобы сооб-
щить об инверсии, выберем -X, а для указания о нумерации
нужных строк выберем -N("номер"), то команда
FIND -X -N THE
при входных данных
NOW IS THE TIME
FOR ALL GOOD MEN
TO COME TO THE AID
OF THEIR PARTY.
Должна выдать
2:FOR ALL GOOD MEN
Нужно, чтобы необязательные аргументы могли располагать-
ся в произвольном порядке, и чтобы остальная часть программы
не зависела от количества фактически присутствующих аргумен-
тов. в частности, вызов функции INDEX не должен содержать
ссылку на ARGV[2], когда присутствует один необязательный
аргумент, и на ARGV[1], когда его нет. Более того, для поль-
зователей удобно, чтобы необязательные аргументы можно было
объединить в виде:
FIND -NX THE
вот сама программа:
#DEFINE MAXLINE 1000
MAIN(ARGC, ARGV) /* FIND PATTERN FROM FIRST ARGUMENT */
INT ARGC;
CHAR *ARGV[];
\(
CHAR LINE[MAXLINE], *S;
LONG LINENO = 0;
INT EXCEPT = 0, NUMBER = 0;
WHILE (--ARGC > 0 && (*++ARGV)[0] == '-')
FOR (S = ARGV[0]+1; *S != '\0'; S++)
SWITCH (*S) \(
CASE 'X':
EXCEPT = 1;
BREAK;
CASE 'N':
NUMBER = 1;
BREAK;
DEFAULT:
PRINTF("FIND: ILLEGAL OPTION %C\N", *S);
ARGC = 0;
BREAK;
\)
IF (ARGC != 1)
PRINTF("USAGE: FIND -X -N PATTERN\N");
ELSE
WHILE (GETLINе(LINE, MAXLINE) > 0) \(
LINENO++;
IF ((INDEX(LINE, *ARGV) >= 0) != EXCEPT) \
IF (NUMBER)
PRINTF("%LD: ", LINENO);
PRINTF("%S", LINE);
\)
\)
\)
Аргумент ARGV увеличивается перед каждым необязательным
аргументом, в то время как аргумент ARGC уменьшается. если
нет ошибок, то в конце цикла величина ARGC должна равняться
1, а *ARGV должно указывать на заданную комбинацию. Обратите
внимание на то, что *++ARGV является указателем аргументной
строки; (*++ARGV)[0] - ее первый символ. Круглые скобки
здесь необходимы, потому что без них выражение бы приняло
совершенно отличный (и неправильный) вид *++(ARGV[0]). Дру-
гой правильной формой была бы **++ARGV.
Упражнение 5-7
--------------
Напишите программу ADD, вычисляющую обратное польское
выражение из командной строки. Например,
ADD 2 3 4 + *
вычисляет 2*(3+4).
Упражнение 5-8
--------------
Модифицируйте программы ENTAB и DETAB (указанные в ка-
честве упражнений в главе 1) так, чтобы они получали список
табуляционных остановок в качестве аргументов. Если аргумен-
ты отсутствуют, используйте стандартную установку табуляций.
Упражнение 5-9
--------------
Расширьте ENTAB и DETAB таким образом, чтобы они воспри-
нимали сокращенную нотацию
ENTAB M +N
означающую табуляционные остановки через каждые N столбцов,
начиная со столбца M. Выберите удобное (для пользователя)
поведение функции по умолчанию.
Упражнение 5-10
---------------
Напишите программу для функции TAIL, печатающей послед-
ние N строк из своего файла ввода. Пусть по умолчанию N рав-
но 10, но это число может быть изменено с помощью необяза-
тельного аргумента, так что
TAIL -N
печатает последние N строк. программа должна действовать ра-
ционально, какими бы неразумными ни были бы ввод или значе-
ние N. Составьте программу так, чтобы она оптимальным обра-
зом использовала доступную память: строки должны храниться,
как в функции SORT, а не в двумерном массиве фиксированного
размера.
5.12. Указатели на функции
В языке "с" сами функции не являются переменными, но
имеется возможность определить указатель на функцию, который
можно обрабатывать, передавать другим функциям, помещать в
массивы и т.д. Мы проиллюстрируем это, проведя модификацию
написанной ранее программы сортировки так, чтобы при задании
необязательного аргумента -N она бы сортировала строки ввода
численно, а не лексикографически.
Сортировка часто состоит из трех частей - сравнения, ко-
торое определяет упорядочивание любой пары объектов, перес-
тановки, изменяющей их порядок, и алгоритма сортировки, осу-
ществляющего сравнения и перестановки до тех пор, пока
объекты не расположатся в нужном порядке. Алгоритм сортиров-
ки не зависит от операций сравнения и перестановки, так что,
передавая в него различные функции сравнения и перестановки,
мы можем организовать сортировку по различным критериям.
Именно такой подход используется в нашей новой программе
сортировки.
Как и прежде, лексикографическое сравнение двух строк
осуществляется функцией STRCMP, а перестановка функцией
SWAP; нам нужна еще функция NUMCMP, сравнивающая две строки
на основе численного значения и возвращающая условное указа-
ние того же вида, что и STRCMP. Эти три функции описываются
в MAIN и указатели на них передаются в SORT. В свою очередь
функция SORT обращается к этим функциям через их указатели.
мы урезали обработку ошибок в аргументах с тем, чтобы сосре-
доточиться на главных вопросах.
#DEFINE LINES 100 /* MAX NUMBER OF LINES
TO BE SORTED */
MAIN(ARGC, ARGV) /* SORT INPUT LINES */
INT ARGC;
CHAR *ARGV[];
\(
CHAR *LINEPTR[LINES]; /* POINTERS TO TEXT LINES */
INT NLINES; /* NUMBER OF INPUT LINES READ */
INT STRCMP(), NUMCMP(); /* COMPARSION FUNCTIONS */
INT SWAP(); /* EXCHANGE FUNCTION */
INT NUMERIC = 0; /* 1 IF NUMERIC SORT */
IF(ARGC>1 && ARGV[1][0] == '-' && ARGV[1][1]=='N')
NUMERIC = 1;
IF(NLINES = READLINES(LINEPTR, LINES)) >= 0) \(
IF (NUMERIC)
SORT(LINEPTR, NLINES, NUMCMP, SWAP);
ELSE
SORT(LINEPTR, NLINES, STRCMP, SWAP);
WRITELINES(LINEPTR, NLINES);
\) ELSE
PRINTF("INPUT TOO BIG TO SORT\N");
\)
Здесь STRCMP, NIMCMP и SWAP - адреса функций; так как извес-
тно, что это функции, операция & здесь не нужна совершенно
аналогично тому, как она не нужна и перед именем массива.
Передача адресов функций организуется компилятором.
Второй шаг состоит в модификации SORT:
SORT(V, N, COMP, EXCH) /* SORT STRINGS V[0] ... V[N-1] */
CHAR *V[]; /* INTO INCREASING ORDER */
INT N;
INT (*COMP)(), (*EXCH)();
\(
INT GAP, I, J;
FOR(GAP = N/2; GAP > 0; GAP /= 2)
FOR(I = GAP; I < N; I++)
FOR(J = I-GAP; J >= 0; J -= GAP) \(
IF((*COMP)(V[J], V[J+GAP]) <= 0)
BREAK;
(*EXCH)(&V[J], &V[J+GAP]);
\)
\)
Здесь следует обратить определенное внимание на описа-
ния. Описание
INT (*COMP)()
говорит, что COMP является указателем на функцию, которая
возвращает значение типа INT. Первые круглые скобки здесь
необходимы; без них описание
INT *COMP()
говорило бы, что COMP является функцией, возвращающей указа-
тель на целые, что, конечно, совершенно другая вещь.
Использование COMP в строке
IF (*COMP)(V[J], V[J+GAP]) <= 0)
полностью согласуется с описанием: COMP - указатель на функ-
цию, *COMP - сама функция, а
(*COMP)(V[J], V[J+GAP])
- обращение к ней. Круглые скобки необходимы для правильного
объединения компонентов.
Мы уже приводили функцию STRCMP, сравнивающую две строки
по первому численному значению:
NUMCMP(S1, S2) /* COMPARE S1 AND S2 NUMERICALLY */
CHAR *S1, *S2;
\(
DOUBLE ATOF(), V1, V2;
V1 = ATOF(S1);
V2 = ATOF(S2);
IF(V1 < V2)
RETURN(-1);
ELSE IF(V1 > V2)
RETURN(1);
ELSE
RETURN (0);
\)
Заключительный шаг состоит в добавлении функции SWAP,
переставляющей два указателя. Это легко сделать, непосредст-
венно используя то, что мы изложили ранее в этой главе.
SWAP(PX, PY) /* INTERCHANGE *PX AND *PY */
CHAR *PX[], *PY[];
\(
CHAR *TEMP;
TEMP = *PX;
*PX = *PY;
*PY = TEMP;
\)
Имеется множество других необязятельных аргументов, ко-
торые могут быть включены в программу сортировки: некоторые
из них составляют интересные упражнения.
Упражнение 5-11
---------------
Модифицируйте SORT таким образом, чтобы она работала с
меткой -R, указывающей на сортировку в обратном (убывающем)
порядке. Конечно, -R должна работать с -N.
Упражнение 5-12
---------------
Добавьте необязательный аргумент -F, объединяющий вместе
прописные и строчные буквы, так чтобы различие регистров не
учитывалось во время сортировки: данные из верхнего и нижне-
го регистров сортируются вместе, так что буква 'а' прописное
и 'а' строчное оказываются соседними , а не разделенными це-
лым алфавитом.
Упражнение 5-13
---------------
Добавьте необязательный аргумент -D ("словарное упорядо-
чивание"), при наличии которого сравниваются только буквы,
числа и пробелы. Позаботьтесь о том, чтобы эта функция рабо-
тала и вместе с -F.
Упражнение 5-14
---------------
Добавьте возможность обработки полей, так чтобы можно
было сортировать поля внутри строк. Каждое поле должно сор-
тироваться в соответствии с независимым набором необязатель-
ных аргументов. (предметный указатель этой книги сортировал-
ся с помощью аргументов -DF для категории указателя и с -N
для номеров страниц).
* 6. Структуры *
Структура - это набор из одной или более переменных,
возможно различных типов, сгруппированных под одним именем
для удобства обработки. (В некоторых языках, самый известный
из которых паскаль, структуры называются "записями").
Традиционным примером структуры является учетная карточ-