Язык С


Командная строка аргументов


Системные средства, на которые опирается реализация язы- ка "с", позволяют передавать командную строку аргументов или параметров начинающей выполняться программе. Когда функция 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, а не в двумерном массиве фиксированного размера.




    Содержание раздела