Menu

Аргументы командной строки в C: argc, argv и их безопасный разбор

Как программа на C читает свою командную строку: устройство argc и argv, перебор аргументов, что лежит в argv[0], преобразование чисел через strtol вместо atoi и маленький калькулятор, который собирает всё воедино.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Любой программе, запущенной из терминала, можно передать аргументы: 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завершающий признак
argc3количество, считая 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 для некорректного ввода, что неотличимо от настоящего нуля. Используйте strtolstrtod для чисел с плавающей точкой) всякий раз, когда данные приходят извне вашей программы. Подробнее — в преобразовании строк.

Маленький калькулятор

Собираем всё вместе — программа принимает число, оператор и число:

Попробуйте 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])), чтобы сообщение соответствовало тому, как пользователь на самом деле вызвал программу. В редких случаях там может оказаться пустая строка, поэтому не считайте его гарантированно заданным.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ