Как передать файл в функцию c

Обновлено: 21.11.2024

путь Путь, идентифицирующий новый файл образа процесса. argv Массив указателей символов на строки, заканчивающиеся NULL. Ваше приложение должно гарантировать, что последний член этого массива является указателем NULL. Эти строки составляют список аргументов, доступных для нового образа процесса. Значение в argv[0] должно указывать на имя файла, связанное с запускаемым процессом.

Библиотека:

Используйте параметр -l c для qcc, чтобы связать эту библиотеку. Эта библиотека обычно включается автоматически.

Описание:

Функция execv() заменяет текущий образ процесса новым образом процесса, указанным path. Новый образ создается из обычного исполняемого файла, называемого новым файлом образа процесса. Возврата не происходит, поскольку образ вызывающего процесса заменяется новым образом процесса.

Когда в результате этого вызова выполняется программа на языке C, она вводится как вызов функции на языке C следующим образом:

где argc — количество аргументов, а argv — массив указателей символов на сами аргументы. Кроме того, следующая переменная:

инициализируется как указатель на массив указателей символов на строки окружения. Массивы argv и environ заканчиваются нулевым указателем. Нулевой указатель, завершающий массив argv, не учитывается в argc.

Многопоточные приложения не должны использовать переменную environ для доступа или изменения любой переменной среды, в то время как любой другой поток одновременно изменяет любую переменную среды. Вызов любой функции, зависящей от любой переменной среды, считается использованием переменной environ для доступа к этой переменной среды.

Аргументы, указанные программой с одной из функций exec, передаются новому образу процесса в соответствующих аргументах main().

Среда для нового образа процесса берется из внешней переменной environ в вызывающем процессе.

Количество байтов, доступных для комбинированных списков аргументов и сред нового процесса, составляет ARG_MAX .

Файловые дескрипторы, открытые в вызывающем образе процесса, остаются открытыми в новом образе процесса, за исключением случаев, когда установлен флаг FD_CLOEXEC функции fcntl(). Для тех файловых дескрипторов, которые остаются открытыми, все атрибуты описания открытого файла, включая файловые блокировки, остаются неизменными. Если дескриптор файла закрыт по этой причине, блокировки файлов удаляются, как описано в close(), в то время как блокировки, на которые не влияет close(), не изменяются.

Потоки каталогов, открытые в вызывающем образе процесса, закрываются в новом образе процесса.

Сигналы, установленные в SIG_DFL в вызывающем процессе, устанавливаются в качестве действия по умолчанию в новом образе процесса. Сигналы, установленные вызывающим образом процесса в SIG_IGN, игнорируются новым образом процесса. Сигналы, настроенные для перехвата вызывающим образом процесса, устанавливаются в качестве действия по умолчанию в новом образе процесса. После успешного вызова альтернативные стеки сигналов не сохраняются, а флаг SA_ONSTACK сбрасывается для всех сигналов.

После успешного вызова все функции, ранее зарегистрированные с помощью atexit(), больше не регистрируются.

Если путь находится в файловой системе, смонтированной с установленным флагом ST_NOSUID, действующий идентификатор пользователя, действующий идентификатор группы, сохраненный идентификатор пользователя и сохраненный идентификатор группы не изменяются для нового процесса. . В противном случае, если установлен бит режима set-user ID, эффективный идентификатор пользователя нового образа процесса устанавливается равным идентификатору пользователя path. Точно так же, если установлен бит режима set-group ID, эффективный идентификатор группы нового процесса устанавливается равным идентификатору группы path. Идентификатор реального пользователя, идентификатор реальной группы и идентификаторы дополнительных групп нового процесса остаются такими же, как и у вызывающего процесса. Действующий идентификатор пользователя и эффективный идентификатор группы нового образа процесса сохраняются (как сохраненный идентификатор пользователя набора и сохраненный идентификатор группы набора, используемые setuid()).

Любые сегменты общей памяти, прикрепленные к вызывающему образу процесса, не присоединяются к новому образу процесса.

Новый процесс также наследует как минимум следующие атрибуты образа вызывающего процесса:

  • идентификатор процесса
  • идентификатор родительского процесса
  • идентификатор группы процессов
  • членство в сеансе
  • настоящий идентификатор пользователя
  • настоящий идентификатор группы
  • дополнительные идентификаторы групп
  • время, оставшееся до сигнала будильника (см. будильник())
  • текущий рабочий каталог
  • корневой каталог
  • маска создания файлового режима (см. umask())
  • маска сигнала процесса (см. sigprocmask())
  • ожидающий сигнал (см. sigpending())
  • tms_utime, tms_stime, tms_cutime и tms_cstime (см. times())
  • ограничения ресурсов
  • терминал управления
  • интервальные таймеры.

Вызов этой функции из процесса с более чем одним потоком приводит к завершению всех потоков и загрузке и выполнению нового исполняемого образа. Функции деструктора не вызываются.

После успешного завершения поле st_atime файла помечается для обновления. Если exec* завершился неудачно, но смог найти файл образа процесса, не указано, помечено ли поле st_atime для обновления. В случае успеха файл образа процесса считается открытым с помощью open(). Соответствующий close() считается происходящим в момент времени после этого открытия, но до завершения процесса или успешного завершения последующего вызова одной из функций exec*.< /p>

exec*() сводка

< td>execl()< td>execvp() < tr>
Функция Описание POSIX?
список аргументов, заканчивающийся NULL Да
execle () список аргументов, заканчивающийся NULL, укажите среду нового процесса Да
execlp () список аргументов, заканчивающийся NULL, поиск нового процесса в PATH Да
execlpe() NULL-терминированный список аргументов, поиск нового процесса в PATH, указание среды нового процесса Нет
execv() массив аргументов, заканчивающийся NULL Да
execve() массив аргументов, заканчивающийся NULL, укажите среду нового процесса Да
массив аргументов, заканчивающийся NULL, поиск нового процесса в PATH Да
execvpe() NULL -терминированный массив аргументов, поиск нового процесса в PATH, указание окружения нового процесса Нет

Возвращает:

При успешном выполнении execv() он не возвращается; в противном случае возвращается -1 и устанавливается errno.

Ошибки:

E2BIG Список аргументов и среда превышают системный предел ARG_MAX байт. EACCESS Вызывающий процесс не имеет разрешения на поиск в каталоге, указанном в path, или у него нет разрешения на выполнение path или path файловая система была смонтирована с флагом ST_NOEXEC. ELOOP Слишком много уровней символических ссылок или префиксов. ENAMETOOLONG Длина path или элемента переменной среды PATH превышает PATH_MAX . ENOENT Один или несколько компонентов пути не существуют, или аргумент path указывает на пустую строку. ENOEXEC Файл образа нового процесса имеет правильные права доступа, но имеет неправильный формат. ENOMEM Недостаточно памяти для создания нового процесса. ENOTDIR Компонент path не является каталогом.

Примеры:

Предыдущий код вызывает myprog, как если бы пользователь ввел:

как команду в оболочке. Программа будет найдена, если myprog существует в текущем рабочем каталоге.

Узнайте, как структурировать файл C и написать основную функцию C, которая обрабатывает аргументы командной строки, как чемпион.

Я знаю, Python и JavaScript — это то, с помощью чего современные дети пишут все свои безумные «приложения». Но не спешите сбрасывать со счетов C — это способный и лаконичный язык, который может многое предложить. Если вам нужна скорость, написание на C может быть вашим ответом. Если вы ищете надежную работу и возможность узнать, как выследить разыменование нулевого указателя, C также может быть вашим ответом! В этой статье я объясню, как структурировать файл C и написать основную функцию C, которая обрабатывает аргументы командной строки, как чемпион.

Я: заядлый системный программист Unix.

Вы: у кого-то есть редактор, компилятор C и немного свободного времени.

Скучная, но правильная программа на C

Программа на C начинается с функции main(), обычно хранящейся в файле с именем main.c.

Эта программа компилирует, но ничего не делает.

Правильно и скучно.

Основные функции уникальны

Функция main() — это первая функция в вашей программе, которая выполняется, когда она начинает выполняться, но это не первая выполняемая функция. Первой функцией является _start(), которая обычно предоставляется библиотекой времени выполнения C и автоматически подключается при компиляции вашей программы. Детали сильно зависят от операционной системы и набора инструментов компилятора, поэтому я сделаю вид, что не упомянул об этом.

Функция main() имеет два аргумента, которые традиционно называются argc и argv и возвращают целое число со знаком. Большинство сред Unix предполагают, что программы будут возвращать 0 (ноль) в случае успеха и -1 (отрицательная единица) в случае неудачи.

Аргумент Имя Описание
argc Количество аргументов Длина вектора аргументов
argv Вектор аргументов Массив указателей на символы

Вектор аргумента, argv, представляет собой токенизированное представление командной строки, которая вызвала вашу программу. В приведенном выше примере argv будет списком следующих строк:

Вектор аргументов всегда содержит по крайней мере одну строку в первом индексе, argv[0], которая представляет собой полный путь к выполняемой программе.

Структура файла main.c

Когда я пишу файл main.c с нуля, он обычно имеет следующую структуру:

Я расскажу о каждом из этих пронумерованных разделов, кроме нулевого, ниже. Если вам необходимо указать текст об авторских правах или лицензии в источнике, поместите его туда.

Еще одна вещь, о которой я не буду говорить, — это комментарии.

Вместо комментариев используйте понятные имена функций и переменных.

Апеллируя к присущей программистам лени, добавляя комментарии, вы удваиваете нагрузку на техническое обслуживание. Если вы изменяете или реорганизуете код, вам необходимо обновить или расширить комментарии. Со временем код видоизменяется, и в нем не остается ничего похожего на то, что описывают комментарии.

Если вам нужно написать комментарии, не пишите о том, что делает код. Вместо этого напишите о том, почему код делает то, что делает. Пишите комментарии, которые вам захочется прочитать через пять лет, когда вы все забудете об этом коде. И судьба мира зависит от вас. Никакого давления.

1. Включает

Первое, что я добавляю в файл main.c, — это включения, чтобы сделать множество стандартных функций и переменных библиотеки C доступными для моей программы. Стандартная библиотека C делает много вещей; изучите файлы заголовков в /usr/include, чтобы узнать, что он может сделать для вас.

Это минимальный набор глобальных включений, который я буду включать по умолчанию для следующих вещей:

2. Определяет

Сейчас это не имеет особого смысла, но в определении OPTSTR я укажу, какие переключатели командной строки порекомендует программа. Обратитесь к man-странице getopt(3), чтобы узнать, как OPTSTR повлияет на поведение getopt().

Определение USAGE_FMT представляет собой строку формата в стиле printf(), на которую ссылается функция Usage().

3. Внешние объявления

Внешнее объявление переносит это имя в пространство имен текущей единицы компиляции (она же "файл") и позволяет программе получить доступ к этой переменной. Здесь мы ввели определения для трех целочисленных переменных и указателя на символ. Переменные с префиксом opt используются функцией getopt(), а errno используется в качестве внеполосного канала связи стандартной библиотекой C для сообщения о том, почему функция могла завершиться ошибкой.

4. Определения типов

После внешних объявлений я предпочитаю объявлять определения типов для структур, объединений и перечислений. Именование typedef само по себе является религией; Я настоятельно предпочитаю суффикс _t, чтобы указать, что имя является типом. В этом примере я объявил options_t как структуру с четырьмя членами. C — язык программирования, нейтральный к пробелам, поэтому я использую пробелы для выравнивания имен полей в одном столбце. Мне просто нравится, как это выглядит. Для объявлений указателя я добавляю звездочку к имени, чтобы было понятно, что это указатель.

5. Объявления глобальных переменных

Глобальные переменные — плохая идея, и вы никогда не должны их использовать. Но если вам нужно использовать глобальные переменные, объявите их здесь и обязательно присвойте им значение по умолчанию. Серьезно, не используйте глобальные переменные.

6. Прототипы функций

При написании функций, добавляя их после функции main(), а не перед ней, включайте сюда прототипы функций. Ранние компиляторы C использовали однопроходную стратегию, что означало, что каждый символ (имя переменной или функции), который вы использовали в своей программе, должен был быть объявлен до того, как вы его использовали. Современные компиляторы — это почти все многопроходные компиляторы, которые создают полную таблицу символов перед генерацией кода, поэтому использование прототипов функций не является строго обязательным. Однако иногда вы не можете выбрать, какой компилятор использовать в вашем коде, поэтому напишите прототипы функций и вперед.

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

7. Разбор командной строки

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

При обнаружении известного параметра командной строки происходит поведение, зависящее от параметра. Некоторые параметры имеют аргумент, указанный в OPTSTR с двоеточием в конце. Когда опция имеет аргумент, следующая строка в argv доступна программе через внешнюю переменную optarg. Я использую optarg для открытия файлов для чтения и записи или преобразования аргумента командной строки из строки в целочисленное значение.

  • Инициализировать opterr значением 0, что запрещает getopt выдавать символ ?.
  • Используйте выход(EXIT_FAILURE); или выйти (EXIT_SUCCESS); в середине main().
  • /* NOTREACHED */ — директива lint, которая мне нравится.
  • Используйте return EXIT_SUCCESS; в конце функций, возвращающих int.
  • Явно приводить неявные преобразования типов.

Сигнатура командной строки для этой программы, если бы она была скомпилирована, выглядела бы примерно так:

Фактически, это то, что use() выдаст в stderr после компиляции.

8. Объявления функций

Наконец, я пишу нестандартные функции. В этом примере функция do_the_needful() принимает указатель на структуру options_t. Я проверяю, что указатель параметров не равен NULL, а затем продолжаю проверять входные и выходные элементы структуры. EXIT_FAILURE возвращает значение, если какой-либо из тестов завершается неудачей, и, устанавливая для внешней глобальной переменной errno обычный код ошибки, я сообщаю вызывающему объекту об общей причине. Вызывающий может использовать удобную функцию perror() для выдачи удобочитаемых сообщений об ошибках на основе значения errno.

Функции почти всегда должны каким-то образом подтверждать свои входные данные. Если полная проверка стоит дорого, попробуйте сделать ее один раз и обработайте проверенные данные как неизменяемые. Функция Usage() проверяет аргумент progname, используя условное присваивание в вызове fprintf(). Функция Usage() в любом случае завершится, поэтому я не беспокоюсь об установке errno или создании большого шума из-за использования правильного имени программы.

Большой класс ошибок, которых я пытаюсь избежать, — это разыменование указателя NULL. Это заставит операционную систему отправить специальный сигнал моему процессу под названием SYSSEGV, что приведет к неминуемой смерти. Последнее, что хотят видеть пользователи, — это сбой из-за SYSSEGV. Гораздо лучше поймать указатель NULL, чтобы выдавать более качественные сообщения об ошибках и корректно завершать работу программы.

Некоторые люди жалуются на наличие нескольких операторов return в теле функции. Они приводят аргументы о «непрерывности потока управления» и других вещах. Честно говоря, если что-то пойдет не так в середине функции, самое время вернуть условие ошибки. Написание множества вложенных операторов if только для одного возврата никогда не является «хорошей идеей».™

Наконец, если вы пишете функцию, которая принимает четыре или более аргументов, рассмотрите возможность объединения их в структуру и передачи указателя на структуру. Это делает сигнатуры функций более простыми, облегчает их запоминание и помогает избежать ошибок при последующем вызове. Это также немного ускоряет вызов функции, поскольку в кадр стека функции нужно копировать меньше элементов. На практике это становится важным, только если функция вызывается миллионы или миллиарды раз. Не беспокойтесь об этом, если это не имеет смысла.

Подождите, вы сказали, что комментариев нет.

В функции do_the_needful() я написал особый тип комментария, предназначенный для использования в качестве заполнителя, а не для документирования кода:

Когда вы находитесь в зоне, иногда вы не хотите останавливаться и писать какой-то особенно корявый код. Ты вернешься и сделаешь это позже, но не сейчас. Вот где я оставлю себе немного панировочных сухарей. Я вставляю комментарий с префиксом XXX и короткое замечание, описывающее, что нужно сделать. Позже, когда у меня будет больше времени, я пройдусь по исходникам в поисках XXX. Неважно, что вы используете, просто убедитесь, что это вряд ли появится в вашей кодовой базе в другом контексте, например, в виде имени функции или переменной.

Собираем все вместе

Хорошо, эта программа по-прежнему почти ничего не делает, когда вы ее компилируете и запускаете. Но теперь у вас есть прочный каркас для создания собственных программ синтаксического анализа командной строки на языке C.

Теперь вы готовы написать C, который будет легче поддерживать. Если у вас есть какие-либо вопросы или отзывы, поделитесь ими в комментариях.

Язык C подобен большинству современных языков программирования тем, что позволяет использовать функции, автономные «модули» кода, которые принимают входные данные, выполняют вычисления и производят выходные данные. Функции C должны быть ТИПИРОВАННЫМИ (тип возвращаемого значения и тип всех указанных параметров).

Как всегда, функция представляет собой модуль кода, который получает информацию (ссылаясь на эту информацию с локальными символическими именами, называемыми параметрами), выполняет некоторые вычисления и (обычно) возвращает новую часть информации на основе информации о параметрах.

Базовый шаблон проектирования функций

Основной синтаксис функции в C см. в главе "Шаблон проектирования функций C".

Файлы Dot C

«Рецепт» функции (код функции) всегда хранится в файле «.C». В C может быть много функций, записанных в одном файле.

Порядок функций в файле

Порядок функций внутри файла произвольный. Неважно, поместите ли вы функцию номер один вверху файла, а функцию номер два внизу или наоборот.

Предостережение: для того, чтобы одна функция могла "видеть" (использовать) другую функцию, "прототип" функции должен быть виден в файле перед использованием. Если функция использует другую функцию, которая в тексте записана над ней в файле, то это автоматически становится истинным. Если функция использует функцию, которая находится «под ней» в файле, то прототип должен располагаться вверху файла. см. прототипы ниже.

Прототип функции

В C все функции должны быть написаны так, чтобы возвращать информацию определенного ТИПА и принимать данные определенного типа (параметры). Эта информация передается компилятору через прототип функции.

Вот синтаксис объявления функции или прототипа:

Прототип может находиться в верхней части файла исходного кода C для описания того, что функция возвращает и что она принимает (тип возвращаемого значения и список параметров). В этом случае (происходит в начале файла) за прототипом функции должна следовать точка с запятой

Прототип функции также используется в начале кода функции. Таким образом, прототип может дважды встречаться в файле исходного кода C. Когда прототип встречается с кодом, НЕ используется точка с запятой.

Основная функция

В C "основная" функция обрабатывается так же, как и любая другая функция, она имеет возвращаемый тип (и в некоторых случаях принимает входные данные через параметры). Единственное отличие состоит в том, что основная функция «вызывается» операционной системой, когда пользователь запускает программу. Таким образом, основная функция всегда является первым кодом, выполняемым при запуске программы.

Примеры функций C:

Тип возвращаемого значения функции C

Каждая функция C должна указывать тип генерируемых данных. Например, приведенная выше функция max возвращает значение типа «двойной». Внутри функции строка «return X;» должен быть найден, где X — значение или переменная, содержащая значение данного типа.

Операция возврата

Когда строка кода в функции говорит: "вернуть X;" выполняется, функция "заканчивается", и код в функции больше не выполняется. Значение X (или значение переменной, представленной X) становится результатом функции.

Вызов функции C (также известный как вызов функции)

Когда одна часть кода вызывает или вызывает функцию, это делается с помощью следующего синтаксиса:

Имя функции должно точно совпадать с именем функции в прототипе функции. Аргументы — это список значений (или переменных, содержащих значения), которые «передаются» в функцию.

Количество аргументов, «переданных» в функцию, должно точно соответствовать количеству параметров, необходимых для функции. Тип каждого аргумента должен точно соответствовать типу каждого параметра. Тип возвращаемой переменной должен точно совпадать с типом возвращаемой функции.

"Переменная" в приведенном выше примере должна иметь тип, эквивалентный типу, возвращаемому функцией. Внутри функции где-то будет строка "return X". Затем значение X копируется в «переменную».

Параметры в функциях C

Параметр — это символическое имя для «данных», которые входят в функцию. В C существует два способа передачи параметров: передача по значению и передача по ссылке.

Передать по значению

Передача по значению означает, что копия данных создается и сохраняется посредством имени параметра. Любые изменения параметра НЕ влияют на данные в вызывающей функции.

Перейти по ссылке

Ссылочный параметр "ссылается" на исходные данные в вызывающей функции. Таким образом, любые изменения, внесенные в параметр, ТАКЖЕ ВНОСЯТСЯ В ИСХОДНУЮ переменную.

Есть два способа передать параметр по ссылке:

В C массивы всегда передаются по ссылке. Любое изменение параметра, содержащего массив, изменит значение исходного массива.

Амперсанд, используемый в прототипе функции.

функция ( & имя_параметра )

Чтобы превратить обычный параметр в параметр передачи по ссылке, мы используем обозначение "& param". Амперсанд (&) — это синтаксис, указывающий C, что любые изменения, внесенные в параметр, также изменяют исходную переменную, содержащую данные.

Пример передачи по значению:

В C по умолчанию используется передача по значению. Например:

Пример передачи по ссылке:

Внимание: C++

Я предлагаю вам использовать компилятор C++, такой как g++, который допускает следующий синтаксис передачи по ссылке (гораздо более современный стиль). Синтаксис заключается в использовании «&» перед именем параметра в объявлении функции. Код вызова и использование внутри функции такие же, как и раньше. Например:

Предупреждение: Стандартный язык C – Использование указателей

В стандартном C вы должны поместить & в место вызова, а не рядом с параметром в объявлении функции; кроме того, вы должны использовать '*' в списке параметров и использовать '*' всякий раз, когда используете параметр внутри функции.

'*' используется для определения "указателя", обсуждение которого выходит за рамки этого простого примера. Не стесняйтесь погуглить "Pointers in C" для длинного трактата о том, как их использовать. или прислушайтесь к моему совету и (как начинающий программист) избегайте их.

Подводя итог, если вы используете ссылочный параметр, любые изменения параметра внутри функции отражаются «вне» функции (т. е. в вызывающей функции)! Если не использовать & (передача по ссылке), то мы получим то же поведение, что и в Matlab (т. е. значение изменяется внутри вызываемой функции, но сохраняет исходное значение в вызывающей функции.

Одна из причин использования ссылочных параметров – сделать программу более "эффективной". Рассмотрим передачу структуры в качестве параметра. Если структура очень большая, и мы копируем ее всю, то мы используем много ненужной памяти.

Пример параметра массива (ВСЕГДА передавать по ссылке)

В C массивы всегда передаются по ссылке. Они не используют обозначение '&', но, тем не менее, передаются по ссылке. Например:

Постоянная ссылка

Для защиты от случайного изменения ссылочного параметра, когда мы действительно хотим, чтобы он не изменялся (мы просто хотим сэкономить время/память), мы можем использовать ключевое слово C const. Например:

Недействительные функции

Если функция не возвращает значение, то используется специальный "ТИП", чтобы сообщить об этом компьютеру. Тип возвращаемого значения – "void" (все строчные буквы).

Функции Void в основном используются в двух классах функций.

Первая функция — это функция, которая выводит информацию для чтения пользователем. Например (для наших целей), функция printf рассматривается как функция void. (На самом деле printf возвращает целое число, равное количеству напечатанных символов, но мы почти всегда игнорируем это значение.)

Все программы C++ должны иметь главную функцию. Если вы попытаетесь скомпилировать программу на C++ без основной функции, компилятор выдаст ошибку. (Библиотеки с динамической компоновкой и статические библиотеки не имеют основной функции.) Основная функция — это место, где ваш исходный код начинает выполнение, но до того, как программа войдет в основную функцию, все статические члены класса без явных инициализаторов обнуляются. В Microsoft C++ глобальные статические объекты также инициализируются перед входом в main. К функции main применяются некоторые ограничения, которые не применяются ни к каким другим функциям C++. Основная функция:

  • Не может быть перегружен (см. Перегрузка функций).
  • Не может быть объявлен встроенным .
  • Не может быть объявлен как статический .
  • Невозможно получить его адрес.
  • Невозможно вызвать из вашей программы.

Сигнатура основной функции

Функция main не имеет объявления, поскольку она встроена в язык. Если бы это было так, синтаксис объявления для main выглядел бы так:

Если в main не указано возвращаемое значение, компилятор возвращает нулевое значение.

Стандартные аргументы командной строки

Аргументы для main позволяют удобно анализировать аргументы в командной строке. Типы для argc и argv определяются языком. Имена argc и argv являются традиционными, но вы можете назвать их как угодно.

Определения аргументов следующие:

argc
Целое число, содержащее количество аргументов, следующих за argv. Параметр argc всегда больше или равен 1.

argv
Массив строк с завершающим нулем, представляющих аргументы командной строки, введенные пользователем программы. По соглашению, argv[0] — это команда, с помощью которой вызывается программа. argv[1] — первый аргумент командной строки. Последний аргумент командной строки — argv[argc - 1] , а argv[argc] всегда равен NULL.

Информацию о том, как отключить обработку командной строки, см. в разделе Настройка обработки командной строки C++.

По соглашению, argv[0] — это имя файла программы. Однако в Windows можно создать процесс с помощью CreateProcess. Если вы используете как первый, так и второй аргументы ( lpApplicationName и lpCommandLine ), argv[0] может не быть именем исполняемого файла. Вы можете использовать GetModuleFileName для получения имени исполняемого файла и его полного пути.

Специальные расширения Microsoft

В следующих разделах описывается поведение Microsoft.

Функция wmain и макрос _tmain

Если вы разрабатываете исходный код для использования расширенных символов Unicode, вы можете использовать точку входа wmain, специфичную для Microsoft, которая представляет собой широкосимвольную версию main . Вот эффективный синтаксис объявления для wmain :

Вы также можете использовать специфичный для Microsoft _tmain , который представляет собой макрос препроцессора, определенный в tchar.h . _tmain разрешается в main, если не определено _UNICODE. В этом случае _tmain преобразуется в wmain. Макрос _tmain и другие макросы, начинающиеся с _t, полезны для кода, который должен создавать отдельные версии как для узкого, так и для широкого набора символов. Дополнительные сведения см. в разделе Использование сопоставлений универсального текста.

Возврат пустоты из main

Как расширение Microsoft, функции main и wmain могут быть объявлены как возвращающие void (без возвращаемого значения). Это расширение также доступно в некоторых других компиляторах, но его использование не рекомендуется. Он доступен для симметрии, когда main не возвращает значение.

Если вы объявите main или wmain как возвращающие void , вы не сможете вернуть код выхода родительскому процессу или операционной системе с помощью оператора return. Чтобы вернуть код выхода, когда main или wmain объявлены как void , вы должны использовать функцию выхода.

Аргумент командной строки envp

Подписи main или wmain позволяют использовать дополнительное расширение Microsoft для доступа к переменным среды. Это расширение также распространено в других компиляторах для систем Windows и UNIX. Имя envp традиционное, но вы можете назвать параметр среды как угодно. Вот эффективные объявления для списков аргументов, которые включают параметр среды:

envp
Необязательный параметр envp представляет собой массив строк, представляющих переменные, установленные в среде пользователя. Этот массив завершается записью NULL. Его можно объявить как массив указателей на char ( char *envp[] ) или как указатель на указатели на char ( char **envp ). Если ваша программа использует wmain вместо main , используйте тип данных wchar_t вместо char .

Блок среды, переданный в main, и wmain представляет собой «замороженную» копию текущей среды. Если позже вы измените среду, выполнив вызов putenv или _wputenv , текущая среда (возвращенная getenv или _wgetenv и переменной _environ или _wenviron) изменится, но блок, на который указывает envp, выиграет. не изменить. Дополнительные сведения о том, как подавить обработку среды, см. в разделе Настройка обработки командной строки C++. Аргумент envp совместим со стандартом C89, но не со стандартами C++.

Пример аргументов для main

В следующем примере показано, как использовать аргументы argc , argv и envp для main :

Анализ аргументов командной строки C++

Правила синтаксического анализа командной строки, используемые кодом Microsoft C/C++, зависят от Microsoft. Код запуска среды выполнения использует эти правила при интерпретации аргументов, заданных в командной строке операционной системы:

Аргументы разделяются пробелом, который является пробелом или табуляцией.

Первый аргумент ( argv[0] ) обрабатывается особым образом. Он представляет собой имя программы. Поскольку это должен быть допустимый путь, допускаются части, заключенные в двойные кавычки ( " ). Двойные кавычки не включаются в вывод argv[0]. Части, заключенные в двойные кавычки, не позволяют интерпретировать пробел или табуляцию. символ в конце аргумента. Последние правила в этом списке не применяются.

Строка, заключенная в двойные кавычки, интерпретируется как один аргумент, который может содержать символы пробела. Строка в кавычках может быть встроена в аргумент. Знак вставки (^) не распознается как escape-символ или разделитель. В строке, заключенной в кавычки, пара двойных кавычек интерпретируется как одна экранированная двойная кавычка. Если командная строка заканчивается до того, как будет найдена закрывающая двойная кавычка, то все прочитанные до сих пор символы выводятся в качестве последнего аргумента.

Двойная кавычка, перед которой стоит обратная косая черта ( \" ), интерпретируется как буквальная двойная кавычка ( " ).

Обратная косая черта интерпретируется буквально, если только перед ней не стоит двойная кавычка.

Если за четным числом обратных косых черт следует двойная кавычка, то одна обратная косая черта ( \ ) помещается в массив argv для каждой пары обратных косых черт ( \\ ), а двойная кавычка ( " ) интерпретируется как разделитель строк.

Если за нечетным числом обратных косых черт следует двойная кавычка, то одна обратная косая черта ( \ ) помещается в массив argv для каждой пары обратных косых черт ( \\ ). Двойная кавычка интерпретируется оставшейся обратной косой чертой как escape-последовательность, в результате чего буквальная двойная кавычка ( " ) помещается в argv .

Пример разбора аргумента командной строки

В следующей программе показано, как передаются аргументы командной строки:

Результаты разбора командной строки

В следующей таблице показаны пример ввода и ожидаемый результат, демонстрирующие правила из предыдущего списка.

< /tr> < /tr>
Ввод командной строки argv[1] argv[2] argv[3]< /th>
"abc" de abc d e
a\\bd"ef"gh a\\b de fg h
a\\\"bcd a\"b c d
a\\\\"bc" de a\\bc d e
a"b"" cd ab" cd

Подстановочное расширение

Компилятор Microsoft дополнительно позволяет использовать подстановочные знаки, вопросительный знак ( ? ) и звездочку ( * ) для указания аргументов имени файла и пути в командной строке.

Аргументы командной строки обрабатываются внутренней процедурой в коде запуска среды выполнения, которая по умолчанию не преобразует подстановочные знаки в отдельные строки в массиве строк argv. Вы можете включить подстановочное расширение, включив файл setargv.obj ( wsetargv.obj файл для wmain ) в параметрах компилятора /link или в командной строке LINK.

Дополнительную информацию о параметрах компоновщика запуска во время выполнения см. в разделе Параметры связывания.

Настройка обработки командной строки C++

Если ваша программа не принимает аргументы командной строки, вы можете отключить подпрограмму обработки командной строки, чтобы сэкономить немного места. Чтобы запретить его использование, включите файл noarg.obj (как для main, так и для wmain ) в параметры компилятора /link или в командную строку LINK.

Аналогичным образом, если вы никогда не обращаетесь к таблице окружения через аргумент envp, вы можете подавить внутреннюю процедуру обработки окружения. Чтобы запретить его использование, включите файл noenv.obj (как для main, так и для wmain ) в параметры компилятора /link или в командную строку LINK.

Ваша программа может вызывать семейство подпрограмм spawn или exec в библиотеке времени выполнения C. Если это так, вам не следует подавлять процедуру обработки среды, поскольку она используется для передачи среды от родительского процесса к дочернему процессу.

Читайте также: