Любой программе, запущенной из терминала, можно передать аргументы: gcc -Wall hello.c -o hello передаёт четыре штуки. Ваши собственные программы на C читают свои аргументы через два параметра main — и это весь механизм: ни вызова библиотеки, ни настройки.
Сигнатура
До сих пор во всех примерах этой документации использовалась форма int main(void). Вторая стандартная форма принимает командную строку:
int main(int argc, char *argv[]) {
/* ... */
}
argc(«argument count», счётчик аргументов) — сколько всего аргументов, включая имя программы.argv(«argument vector», вектор аргументов) — массив строк.char *argv[]означает «массив указателей на char», то есть массив строк C.
char **argv — равнозначная запись, которая встречается ничуть не реже; параметры-массивы вырождаются в указатели, поэтому обе записи объявляют одно и то же.
Задайте аргументы в панели Args редактора (одно поле — один аргумент) и нажмите Run. Попробуйте три поля: hello, world, 42.
Устройство
Запуск ./greet Ada Lovelace даёт:
| Индекс | Значение | |
|---|---|---|
argv[0] | "./greet" | имя программы, как её вызвали |
argv[1] | "Ada" | первый настоящий аргумент |
argv[2] | "Lovelace" | второй настоящий аргумент |
argv[3] | NULL | завершающий признак |
argc | 3 | количество, считая argv[0] |
Четыре факта, которые стоит запомнить:
argcна практике не меньше 1, потому чтоargv[0]— имя программы. Настоящие аргументы начинаются с индекса 1, поэтому циклы по ним стартуют сi = 1.- Всё — строки.
./prog 42даёт вам"42", два символа, а вовсе не число 42. Преобразование — ваша задача. argv[argc]равенNULL. Это гарантировано стандартом, так что при желании массив можно обойти и безargc:for (char **p = argv + 1; *p != NULL; p++).- Слова разбивает оболочка, а не ваша программа.
./prog "Ada Lovelace"— это один аргумент; кавычки съедает оболочка, и доargvони не доходят.
Проверяйте, прежде чем читать
Непереданный аргумент — это не пустая строка, а память, трогать которую вы не имеете права. Всегда сначала проверяйте argc:
В этой крошечной программе два соглашения, оба стандартны для Unix-утилит:
- Сообщение об использовании идёт в
stderr, а не вstdout, чтобы не засорять вывод, который куда-то перенаправляют. - Ненулевой код возврата из
mainсообщает о неудаче тому, кто запустил программу.0— успех; всё остальное означает, что что-то пошло не так. Скрипты оболочки это проверяют.
Вывод argv[0] вместо жёстко прописанного имени означает, что сообщение соответствует тому, как пользователь вызвал программу, — даже если её переименовали или запустили по другому пути.
Преобразование числовых аргументов
argv[1] — это текст. Чтобы считать, его нужно преобразовать, и для этого используется strtol из <stdlib.h>:
Добавьте 10, 20, 30 тремя полями Args, а затем замените одно из них на banana, чтобы увидеть ветку с ошибкой.
Третий параметр strtol — основание системы счисления: 10 для десятичной, 16 для шестнадцатеричной или 0, чтобы определить его автоматически по префиксу 0x или ведущему нулю. Второй — куда записать указатель на первый символ, который функция не разобрала, и именно это делает возможной проверку ошибок:
end == textозначает, что не разобрано ничего: аргумент не начинался с числа.*end != '\0'означает, что после числа остался текст, поэтому"12abc"отвергается, а не превращается втихую в 12.errno == ERANGEозначает, что значение переполнилоlong.
Сравните с atoi, который короче и не может сообщить ничего из этого:
int n = atoi(argv[1]); /* "banana" -> 0, "0" -> 0, переполнение -> неопределённое поведение */
atoi возвращает 0 для некорректного ввода, что неотличимо от настоящего нуля. Используйте strtol (и strtod для чисел с плавающей точкой) всякий раз, когда данные приходят извне вашей программы. Подробнее — в преобразовании строк.
Маленький калькулятор
Собираем всё вместе — программа принимает число, оператор и число:
Попробуйте 12 + 30, затем 7 x 6, затем 5 / 0.
Обратите внимание на strcmp(op, "+") == 0 вместо op == "+". Сравнение значений char * через == сравнивает адреса, а не содержимое, и дало бы false даже для одинакового текста. Смотрите функции работы со строками.
x вместо * — деталь из реальной жизни: большинство оболочек разворачивают одиночную * в список файлов текущего каталога ещё до того, как ваша программа запустится. Экранирование ('*') тоже работает, но выбрать символ, который оболочка игнорирует, дружелюбнее.
Разбор флагов
Опции по традиции начинаются с -. Простой цикл, написанный вручную, покрывает типичные случаи:
Попробуйте с четырьмя полями Args: -v, -n, 3, hello.
Проверка i + 1 >= argc перед argv[++i] — самая важная строка: опция, ожидающая значение, обязана убедиться, что значение вообще есть, иначе -n последним аргументом прочитает за концом массива. В настоящих Unix-системах всё это делает за вас getopt из <unistd.h>, включая слипшиеся короткие опции вроде -vn3; к нему стоит обращаться, как только у программы становится больше двух-трёх флагов.
Аргументы в браузерном редакторе
Блоки-редакторы на этой странице компилируются и запускаются за вас, а панель Args — это место, куда вы вводите то, что в терминале шло бы после имени программы. Каждое поле, добавленное кнопкой Add arg, становится ровно одним элементом argv: оболочки между ними нет, поэтому ничего не разбивается по пробелам и кавычки не нужны — поле со значением hello world приходит как единственный аргумент hello world. Чтобы передать -n 3 hello, добавьте три поля. Пустая панель даёт argc == 1 — поэтому защита argc < 2 срабатывает при первом запуске и поэтому её стоит писать.
Частые ошибки
- Чтение
argv[1]без проверкиargc. Неопределённое поведение, если аргументы не переданы, — и самое частое падение в этой области. - Начинать цикл с
i = 0. Так имя программы обрабатывается как данные. - Считать
argv[1]числом. Это текст, пока вы его не преобразуете. - Использовать
atoiдля пользовательского ввода. Нет способа отличить плохой аргумент от нуля. - Сравнивать строки через
==. Используйтеstrcmp. - Писать в строки
argv. Стандарт это разрешает, но место там ограничено и зависит от платформы; копируйте в собственный буфер, если что-то нужно изменить.
Часто задаваемые вопросы
Что такое argc и argv в C?
Это два параметра функции int main(int argc, char *argv[]). argc — количество аргументов командной строки, включая имя программы; argv — массив этих аргументов в виде строк. argv[0] — имя программы, argv[1] — первый настоящий аргумент, а argv[argc] всегда равен NULL.
Как передать аргументы программе на C?
Напишите их после имени исполняемого файла в терминале: ./program hello 42. Это даст argc == 3, где argv[1] — строка "hello", а argv[2] — строка "42". В редакторе на этой странице аргументы вводятся в панель Args, а не в терминал.
Как преобразовать аргумент командной строки в int в C?
Используйте strtol: char *end; long n = strtol(argv[1], &end, 10);, затем проверьте, что end не указывает по-прежнему на начало (ничего не разобрано) и что *end равен '\0' (нет мусора в конце). atoi короче, но возвращает 0 и для "0", и для "banana", поэтому не может сообщить об ошибке.
Что такое argv[0] в C?
Имя, под которым была запущена программа, — обычно ./program или полный путь. Оно полезно в сообщениях об использовании (fprintf(stderr, "usage: %s FILE\n", argv[0])), чтобы сообщение соответствовало тому, как пользователь на самом деле вызвал программу. В редких случаях там может оказаться пустая строка, поэтому не считайте его гарантированно заданным.