Системные средства, на которые опирается реализация язы-ка "с", позволяют передавать командную строку аргументов илипараметров начинающей выполняться программе. Когда функцияMAIN вызывается к исполнению, она вызывается с двумя аргу-ментами. Первый аргумент (условно называемый ARGC) указываетчисло аргументов в командной строке, с которыми происходитобращение к программе; второй аргумент (ARGV) является ука-зателем на массив символьных строк, содержащих эти аргумен-ты, по одному в строке. Работа с такими строками - это обыч-ное использование многоуровневых указателей. Самую простую иллюстрацию этой возможности и необходимыхпри этом описаний дает программа ECHO, которая просто печа-тает в одну строку аргументы командной строки, разделяя ихпробелами. Таким образом, если дана команда ECHO HELLO, WORLD то выходом будет HELLO, WORLD по соглашению ARGV[0] является именем, по которому вызывает-ся программа, так что 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<ARGC-1) ? ' ' : '\N');\) Поскольку ARGV является указателем на массив указателей, тосуществует несколько способов написания этой программы, ис-пользующих работу с указателем, а не с индексацией массива.Мы продемонстрируем два варианта. MAIN(ARGC, ARGV) /* ECHO ARGUMENTS; 2ND VERSION */INT ARGC;CHAR *ARGV[];\( WHILE (--ARGC > 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, а не в двумерном массиве фиксированногоразмера.