Иногда бывает, что при запуске программы данные поступают из командной строки. Эти данные известны под названием аргументы командной строки. Пример:

./a.out test.txt
ls -lt /home/peter/

В данном примере вызываются программы: a.out из текущей папки и ls из каталога, указанного в переменной окружения PATH. Первая программа получает один аргумент — test.txt, тогда как вторая принимает два: -lt и /home/peter/.

Если программа написана на языке C, то при ее запуске сразу передается управление функции main(), которая получает аргументы командной строки и присваивает их своим параметрам.

Ранее мы определяли функцию main() как не принимающую параметры и не возвращающую значение. Но в языке C по умолчанию (если явно не указано иное) любая функция возвращает целое число. Этот факт можно подтвердить, написав код:

main() {
    printf("Hi\n");
    return 0;
}

Это не вызовет предупреждений или ошибок при компиляции. То же самое произойдет и с int main(). Это показывает, что функция по умолчанию возвращает целое число, а не ничто (как void). Однако вы можете изменить возвращаемое значение, например, на void main() или float main().

При запуске программы из командной строки она получает:

  1. Целое число, показывающее количество слов (это элементы, разделенные пробелами) в командной строке,
  2. Указатель на массив строк, где каждая строка — это отдельное слово из командной строки.

Важно, что имя программы тоже учитывается. Например, если команда такова:

./a.out 12 theme 2

тогда первый аргумент будет равен 4, а массив строк — {"./a.out", "12", "theme", "2"}.

Чтобы классифицировать терминологию, у программы всего два аргумента (число и массив), но много аргументов командной строки. Эти аргументы преобразуются в параметры функции main().
Данные (число и указатель) передаются независимо от того, передаются ли какие-либо аргументы программе из командной строки. Даже при запуске просто как ./a.out, первый аргумент равен 1, а второй указывает на массив из одной строки {"./a.out"}.

Передача данных в программу не обязывает функцию main() обязательно их принимать. Если main() определена без параметров, доступ к аргументам невозможен. Но никаких проблем не будет, если попытаться их передать — ошибки не возникнет.

Чтобы получить доступ к переданным данным, их следует передать в переменные. Так как аргументы сразу передаются в main(), у нее должен быть следующий заголовок:
main (int n, char *arr[])

Переменная n содержит количество слов, а arr — указатель на массив строк. Параметр часто записывают как **arr; это эквивалентно. Напоминаем, что массив строк содержит указатели на строки как свои элементы, и в функцию передается указатель на первый указатель массива, что делает **arr.

Задание:

Напишите программу, которая:

#include <stdio.h>
 
int main(int argc, char **argv) {
    int i;
 
    printf("%d\n", argc);
 
    for (i=0; i < argc; i++)
        puts(argv[i]);
}

Отображает количество слов в командной строке и каждое слово с новой строки. Попробуйте её без аргументов и с ними.

В этой программе применяются переменные-параметры argc и argv. Использование этих имен стало стандартом, но они могут быть заменены на любые другие. Однако лучше придерживаться стандарта, чтобы ваш код был более понятен другим программистам.

Практическое значение передачи данных в программу

Если вы работали с командной строкой GNU/Linux, вы знаете, что большинство команд имеют ключи и сопровождаются аргументами. Например, при выполнении команд для просмотра содержимого каталогов, копирования или перемещения указываются объекты файловой системы, над которыми происходит действие, а особенности выполнения команды определяются ключами. Пример:

cp -r ../les_1 ../les_101 

Здесь `cp` — название команды, `-r` — ключ, а `../les_1` и `../les_101` — аргументы команды.

Чаще всего программы получают адреса файлов и "переключатели" (ключи), задающие параметры выполнения программы.

Вот пример программы, которая открывает файлы, указанные пользователем с командной строки на запись или добавление, и записывает или добавляет в них текст, введенный пользователем:

#include <stdio.h>
#include <string.h>
 
main (int argc, char **argv) {
	int i, ch;
	FILE *f[5];
 
	if (argc < 3 || argc > 7) {
		puts("Неверное количество параметров");
		return 1;
	}
 
	if (strcmp(argv[1], "-w") != 0 && strcmp(argv[1], "-a") != 0) {
		puts("Первый параметр может быть либо -w, либо -a");
		return 2;
	}
 
	for (i=0; i < argc-2; i++){
		f[i] = fopen(argv[i+2], argv[1]+1);
		if (f[i] == NULL) {
			printf("Файл %s нельзя открыть\n", argv[i+2]);
			return 3;
		}
	}
 
	while ((ch = getchar()) != EOF)
		for (i=0; i < argc-2; i++)
			putc(ch,f[i]);
 
	for (i=0; i < argc-2; i++)
		fclose(f[i]);
 
	return 0;
}

Объяснения к коду:

  1. Задается массив, который может содержать до пяти файловых указателей. Это ограничивает число файлов, которые могут одновременнно быть открыты на запись, до пяти. Указатель для первого файла будет находиться в f[0], для второго в f[1], и так далее.
  2. Проверяется количество полученных аргументов командной строки. Их должно быть три или больше: имя программы, режим открытия файла, и хотя бы один файл для записи. Поскольку можно открывать до пяти файлов, число аргументов не может превышать семь. Если передано меньше трех или больше семи аргументов, программа завершится, так как оператор return приведет к выходу из функции даже при наличии кода после него. Возврат ненулевого значения из функции может интерпретироваться родительским процессом как индикатор ошибки завершения.
  3. Происходит проверка второго аргумента командной строки. Убедитесь, что он равен «-w» или «-a», иначе условие во втором операторе if будет истинным (равным 1). Метод strcmp() сравнивает строки и возвращает 0, если они идентичны.
  4. Файлы открываются с помощью цикла for, начиная с третьего значения в массиве argv, из-за чего к i добавляют 2. Выражение argc-2 определяет количество имён файлов, где argc — общее количество аргументов, включая первые два, которые не являются именами файлов.
  5. Если argv[1] равно «-w» или «-a», argv[1]+1 вырезает подстроку, оставляя лишь вторую букву. Указатель argv[1] указывает на первый символ строки, а прибавление единицы перемещает указатель на следующий символ.
  6. Если открыть файл не удается, функция fopen() возвращает NULL, в этом случае выполнение программы прекращается.
  7. Каждый ввод с клавиатуры записывается во все открытые файлы.
  8. По завершении все файлы закрываются.

Вопросы для самопроверки:

  1. Что такое аргументы командной строки и приведите пример их использования?
  2. Какой функции передаются аргументы командной строки в программе на языке C?
  3. Что произойдет, если программу запустить как ./a.out без дополнительных аргументов?
  4. Что возвращает функция main() по умолчанию, если тип возвращаемого значения не указан?
  5. Что произойдет, если файл не удастся открыть в программе, работающей с файлами?

Программа курса:

  1. Описание курса
  2. Введение в язык программирования C
  3. Типы данных в C и форматированный вывод
  4. Символьные типы и управляющие символы в C
  5. Операторы ветвления и switch в C
  6. Циклы и операторы в языке C
  7. Битовые операции в языке C
  8. Посимвольный ввод и вывод в C - буферизация
  9. Переменные, адреса и указатели в C
  10. Передача аргументов по ссылке и значению в C
  11. Форматированный ввод данных с использованием scanf
  12. Генерация псевдослучайных чисел на C
  13. Адресная арифметика в массивах C
  14. Передача массивов в функции и указатели
  15. Строки в языке C - особенности и функции работы
  16. Функции работы со строками в C
  17. Работа со структурами в C - создание и применение
  18. Динамические структуры данных в C
  19. Ввод и вывод данных из файлов в языке C
  20. Передача аргументов в C и работа с файлами
  21. Препроцессор в языке C - директивы и макросы
  22. Создание и компиляция многофайловых программ в C
  23. Использование статических и динамических библиотек в C