Опубликован: 05.07.2006 | Уровень: для всех | Доступ: платный
Лекция 6:

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

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

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

Ярослав Воробей
Ярослав Воробей
Россия
Дмитрий Левин
Дмитрий Левин
Россия