В середовищах, що підтримують C, існує спосіб передачі аргументів командного рядка, або параметрів, програмі, коли вона починає своє виконання. Головну функцію main викликано з двома аргументами. Перший (звично, названий argc, як скорочення від «argument count» — відлік аргументів) вказує на кількість аргументів командного рядка з яким було викликано програму; другий (argv, «argument vector» — вектор аргументів) являється покажчиком на масив символьних ланцюжків, що, власне, містить аргументи — кожний ланцюжок відповідає аргументові. Ми, звичайно, використаємо багаторівневі покажчиків для маніпуляції цих символьних ланцюжків. Найпростішою ілюстрацією є програма echo, яка відлунює аргументи командного рядка на одному рядкові, розділюючи їх пробілами. Тобто, команда
echo hello, world
виводить
hello, world
За домовленістю, argv[0] являється назвою за якою було викликано програму, тож argcдорівнює, щонайменше, 1. Якщо argc дорівнює 1, після назви програми на командному рядкові немає аргументів. У прикладі вище, argc дорівнює 3, тоді як argv[0], argv[1] і argv[2]відповідають "echo", "hello," та "world". Першим необов'язковим аргументом є argv[1], а останнім - argv[argc-1]; на додачу, стандарт вимагає, щоб argv[argc] був нульовим покажчиком.
argv:
+-----+ +-----+ +--------+
| *--|----->| *--|----->| echo\0 |
+-----+ | | +--------+
| | +----------+
| *--|----->| hello,\0 |
| | +----------+
| | +---------+
| *--|----->| world\0 |
| | +---------+
| 0 |
+-----+
Наша перша версія 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 спадає; коли вона стає нулем, це означає, що аргументів для виводу на екран більше не залишилось.
Альтернативно, ми могли би написати вираз із printf як наступне:
printf((argc > 1) ? "%s " : "%s", *++argv);
Це демонструє, що формат аргументу printf може також бути виразом. Як ще один приклад, давайте покращимо програму знаходження за шаблоном із Розділу 4.1. Якщо ви пам'ятаєте, ми розмістили шаблон пошуку глибоко всередині програми — явно незадовільне розташування. Слідуючи прикладові UNIX-програми grep, давайте поліпшимо нашу, тож шаблон вказуватиметься як перший аргумент на командному рядкові.
#include <stdio.h>
#include <string.h>
#define MAXLINE 1000
int getline(char *line, int max);
/* find: виводить рядки, що збіглися із шаблоном, вказаним
* у 1-у аргументі */
main(int argc, char *argv[])
{
char line[MAXLINE];
int found = 0;
if (argc != 2)
printf("Usage: find pattern\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, якщо жодного не знайдено. Її оголошено в <string.h>. Цю модель можна розвинути далі, для подальшої ілюстрації конструкцій з покажчиками. Скажімо, ми хотіли би дозволити два необов'язкових аргументи. Один вказуватиме «вивести всі рядки за винятком тих, що збігаються з шаблоном», а другий — «додати попереду кожного виведеного рядка його порядковий номер».
Загальною умовністю C-програм на UNIX-системах є те, що аргумент, який починається зі знака «мінус» означає необов'язковий прапорець або параметр. Якщо ми оберемо -x (як скорочення для «except») для вказівки протилежного результату, та -n («number»), для нумерації рядків, тоді команда
find -x -n шаблон
виведе кожний рядок, що не зійшовся з шаблоном, вказавши його порядковий номер. Необов'язкові аргументи треба дозволити у довільній послідовності і решта програми повинна бути незалежною від кількості аргументів, які ми надамо. Більше того, користувачам зручніше, якщо опції можна буде комбінувати, як от
find -nx шаблон
А ось і сама програма:
#include <stdio.h>
#include <string.h>
#define MAXLINE 1000
int getline(char *line, int max);
/* find: виводить рядки, що збігаються із шаблоном,
* вказаним у 1-му аргументові */
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: illegal option %c\n", c);
argc = 0;
found = -1;
break;
}
if (argc != 1)
printf("Usage: find -x -n pattern\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 зменшено, а 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), щоб вони могли взяти список табуляторних обмежувачів як аргумент. Використайте стандартні установлення табуляції, якщо аргументи відсутні.
Вправа 5-12. Розширте entab і detab, щоб вони дозволяти скорочення
entab -m +n
що означає табуляторний обмежувач кожні n стовпчиків, починаючи зі стовпчика m. Виберіть зручне (для користувача) уставне поводження.
Вправа 5-13. Напишіть програму tail, яка виводить останні n рядків свого вводу. Без задання,n буде задано як 10, скажімо, але його можна змінити за допомогою аргументу на командному рядку, тож
tail -n
виведе останні n рядків вводу. Програма повинна поводитись розумно, незалежно від того якими непередбачуваними є ввід і значення n. Напишіть цю програму таким чином, щоб вона найкраще використовувала місце зберігання; рядки повинні зберігатися як у програмі сортування з Розділу 5.6, а не в двовимірному масиві сталого розміру.