netlib.narod.ru | < Назад | Оглавление | Далее > |
В операционной среде, обеспечивающей поддержку Си, имеется возможность передать аргументы или параметры запускаемой программе с помощью командной строки. В момент запуска программы функции main передаются два аргумента. В первом, обычно называемом argc (сокращение от argument count), стоит количество аргументов, заданых в командной строке. Второй параметр, называемый argv (от argument vector), является указателем на массив символьных строк, содержащих сами аргументы. Для работы с этими строками обычно используются указатели нескольких уровней.
Простейший пример — программа echo (эхо), которая печатает аргументы своей командной строки в одной строчке, отделяя их друг от друга пробелами. Так, команда
echo Здравствуй, мир!
напечатает
Здравствуй, мир!
По общепринятому соглашению argv[0] есть имя вызываемой программы, так что значение argc никогда не бывает меньше 1. Если argc равно 1, то в командной строке после имени программы никаких аргументов нет. В нашем примере argc равно 3, и соответственно argv[0], argv[1] и argv[2] представляют собой строки "echo", "Здравствуй," и "мир!". Первый необязательный аргумент — это argv[1], последний — argv[argc-1]. Кроме того, стандарт требует, чтобы argv[argc] всегда был пустым указателем (NULL).
Первая версия программы echo трактует argv как массив указателей на строки.
#include <stdio.h> /* Эхо аргументов командной строки, версия 1 */ main(int argc, char *argv[]) { int i; for (i = 1; i < argc; i++) printf("%s%s", argv[i], (i < argc-1) ? " " : ""); printf("\n"); return 0; }
Так как argv — это указатель на массив указателей, мы можем работать с ним как с указателем, а не как с индексируемым массивом. Следующая программа основана на приращении argv, он увеличивается таким образом, что его значение в каждый отдельный момент указывает на очередной указатель на char; перебор указателей заканчивается, когда исчерпан argc.
#include <stdio.h> /* Эхо аргументов командной строки. Версия 2 */ main(int argc, char *argv[]) { while (--argc > 0) printf("%s%s", *++argv, (argc > 1) ? " " : ""); printf("\n"); return 0; }
Аргумент argv — указатель на начало массива строк аргументов. Использование в ++argv префиксного оператора ++ приведет к тому, что первым будет напечатан argv[1], а не argv[0]. Каждое очередное приращение указателя дает нам следующий аргумент, на котроый указывает *argv. В это же время значение argc уменьшается на 1, и, когда оно станет равно нулю, все аргументы будут напечатаны.
Инструкцию printf можно было бы написать и так:
printf((argc > 1) ? "%s " : "%s", *++argv);
Как видите, формат в printf тоже может быть выражением.
В качестве второго примера возьмем программу поиска образца, рассмотренную в параграфе 4.1, и несколько усовершенствуем ее. Если вы помните, образец для поиска мы «вмонтировали» глубоко в программу, а это, очевидно, не лучшее решение. Построим нашу программу по аналогии с grep из UNIX, т.е. так, чтобы образец для поиска задавался первым аргументом в командной строке.
#include <stdio.h> #include <string.h> #define MAXLINE 1000 int getline(char *line, int max); /* find: печать строк с образцом, задаваемым первым аргументом */ main(int argc, char *argv[]) { char line[MAXLINE]; int found = 0; if (argc != 2) printf("Используйте в find образец.\n"); else while (getline(line, MAXLINE) > 0) if (strstr(line, argv[1]) != NULL) { printf("%s", line); found++; } return found; }
Стандартная функция strstr(s, t) возвращает указатель на первую встретившуюся строку t в строке s или NULL, если таковой в s не встретилось. Функция объявлена в заголовочном файле <string.h>.
Эту модель можно развивать и дальше, чтобы проиллюстрировать другие операции с указателями. Предположим, что мы вводим еще два необязательных аргумента. Один из них предписывает печатать все строки, кроме тех, в которых встречается образец; второй — перед каждой выводимой строкой печатать ее порядковый номер.
По общему соглашению для Си-программ в системе UNIX знак минус перед аргументом указывает на то, что это необязательный признак или параметр. Так, если -x служит признаком слова «кроме», которое изменяет задание на противоположное, а -n указывает на необходимость нумерации строк, то команда
find -x -n образец
напечатает все строки, в которых не найден указанный образец, и, кроме того, перед каждой строкой укажет ее номер.
Необязательные аргументы разрешается располагать в любом порядке, при этом лучше, чтобы остальная часть программы не зависела от числа представленных аргументов. Кроме того, пользователю было бы удобно, если бы он мог комбинировать необязательные аргументы, например так:
find -nx образец
А теперь напишем нашу программу.
#include <stdio.h> #include <string.h> #define MAXLINE 1000 int getline(char *line, int max); /* find: печать строк с образцами заданными аргументом. Вариант 2 */ main(int argc, char *argv[]) { char line[MAXLINE]; long lineno = 0; int c, except = 0, number = 0, found = 0; while (--argc > 0 && (*++argv)[0] == '-') while (c = *++argv[0]) switch (c) { case 'x': except = 1; break; case 'n': number = 1; break; default: printf("find: неверный параметр %c\n", c); argc = 0; found = -1; break; } if (argc != 1) printf("Используйте: find -x -n образец\n"); else while (getline(line, MAXLINE) > 0) { lineno++; if ((strstr(line, *argv) != NULL) != except) { if (number) printf("%ld:", lineno); printf("%s", line); found++; } } return found; }
Перед получением очередного аргумента argc уменьшается на 1, а argv «перемещается» на следующий аргумент. После завершения цикла при отсутствии ошибок argc содержит количество еще не обработанных аргументов, а argv указывает на первый из них. Таким образом, в конце концов argc должен быть равен 1, а *argv указывать на образец. Заметим, что *++argv является указателем на аргумент-строку, а (*++argv)[0] — его первым символом, на который можно сослаться и другим способом:
**++argv
Поскольку операция индексирования [] имеет более высокий приоритет, чем * и ++, круглые скобки здесь обязательны, без них выражение трактовалось бы так же, как *++(argv[0]). Именно такое выражение мы применим во внутреннем цикле, где просматриваются символы конкретного аргумента. Во внутреннем цикле выражение *++argv[0] увеличивает указатель argv[0].
Потребность в более сложных выражениях для указателей возникает не так уж часто. Но если такое случится, то разбивая процесс вычисления указателя на два или три шага, вы облегчите восприятие этого выражения.
Упражнение 5-10 |
Напишите программу expr, интерпретирующую обратную польскую запись выражения, задаваемого командной строкой, в который каждый оператор и операнд представлены отдельным аргументом. Например, expr 2 3 4 + * вычисляется так же, как выражение 2 * (3 + 4). |
Упражнение 5-11 |
Усовершенствуйте программы entab и detab (см. упражнения 1.20 и 1.21) таким образом, чтобы через аргументы можно было задавать список позиций табуляции. |
Упражнение 5-12 |
Расширьте возможности entab и detab таким образом, чтобы при обращении вида entab -m +n позиции табуляции начинались с m-й позиции и располагались через каждые n позиций. Разработайте удобный для пользователя вариант поведения программы по умолчанию (когда нет никаких аргументов). |
Упражнение 5-13 |
Напишите программу tail, печатающую n последних введенных строк. По умолчанию значение n равно 10, но при желании n можно задать с помощью аргумента. Обращение вида tail -n печатает n последних строк. Программа должна вести себя осмысленно при любых входных данных и любом значении n. Напишите программу так, чтобы наилучшим образом использовать память; запоминание строк организуйте, как в программе сортировки, описанной в параграфе 5.6, а не на основе двумерного массива с фиксированным размером строки. |
netlib.narod.ru | < Назад | Оглавление | Далее > |