Учитывая стандартное определение для основной программы:
int main(int argc, char *argv[]) {
...
}
В каких обстоятельствах может быть argc
равным нулю в системе POSIX?
Учитывая стандартное определение для основной программы:
int main(int argc, char *argv[]) {
...
}
В каких обстоятельствах может быть argc
равным нулю в системе POSIX?
Да, это возможно. Если вы вызываете свою программу следующим образом:
execl("./myprog", NULL, (char *)NULL);
Или поочередно:
char *args[] = { NULL };
execv("./myprog", args);
Затем в "myprog" argc
будет 0.
Стандарт также специально позволяет использовать 0 argc
как указано в разделе 5.1.2.2.1 относительно запуска программы в размещенной среде:
1 Функция, вызванная при запуске программы, называется
main
. Реализация не объявляет прототипа для этой функции. Он должен быть определен с типом возвратаint
и без параметров:int main(void) { /* ... */ }
или с двумя параметрами (называемыми здесь
argc
иargv
, хотя любые имена могут использоваться, поскольку они являются локальными для функции, в которой они объявлены):int main(int argc, char *argv[]) { /* ... */ }
или эквивалент; или каким-либо другим способом реализации.
2 Если они объявлены, параметры
main
функции должны подчиняться следующим ограничениям:
- Величина
argc
должна быть неотрицательной.argv[argc]
должен быть нулевым указателем....
Заметим также, что это означает, что если argc
равно 0, то argv[0]
гарантированно будет NULL. Как printf
обрабатывает указатель NULL, когда он используется в качестве аргумента для спецификатора %s
указывается в стандарте. Многие реализации будут выводить "(null)" в этом случае, но я не верю, что это гарантировано.
Чтобы добавить к другим ответам, в C (POSIX или нет) нет ничего, что предотвращает вызов функции main() как функции внутри программы.
int main(int argc, int argv[]) {
if (argc == 0) printf("Hey!\n");
else main(0,NULL);
return 0;
}
Да, это может быть ноль, а это означает, что argv[0] == NULL
.
Это соглашение, что argv[0]
- это имя программы. У вас может быть argc == 0
если вы запускаете себя двоичным, например, с помощью семейства execve и не даете никаких аргументов. Вы даже можете указать строку, которая не соответствует имени программы. Поэтому использование argv[0]
для получения имени программы не является полностью надежным.
Обычно оболочка, в которой вы вводите свою командную строку, всегда добавляет имя программы в качестве первого аргумента, но опять же это соглашение. Если argv[0]
== "--help", и вы используете опцию getopt для синтаксического разбора, вы не обнаружите ее, потому что optind
инициализируется 1, но вы можете установить optind
в 0, использовать getopt
и "help" длинный вариант покажет вверх.
Короче говоря: вполне возможно, что argc == 0
(argv[0]
действительно не особенный сам по себе). Это происходит, когда пусковая установка вообще не дает аргументов.
Ранним предложениям требовалось, чтобы значение argc перешло в main() как "одно или большее". Это было обусловлено тем же требованием в проектах стандарта ISO C. Фактически, исторические реализации передали значение нуля, когда аргументы exec не передают аргументы. Это требование было удалено из стандарта ISO C и впоследствии удалено из этого объема POSIX.1-2017. Формулировка, в частности использование слова, требует, чтобы строго соответствующее приложение POSIX передавало по крайней мере один аргумент функции exec, тем самым гарантируя, что argc будет одним или большим при вызове такого приложения. На самом деле это хорошая практика, поскольку многие существующие приложения ссылаются на argv [0], не проверяя сначала значение argc.
Требование к строго соответствующему POSIX-приложению также указывает, что значением, переданным в качестве первого аргумента, является строка имени файла, связанная с запущенным процессом. Хотя в некоторых случаях некоторые существующие приложения передают имя пути, а не строку имени файла, строка имени файла в целом полезна, поскольку общее использование argv [0] - в диагностике печати. В некоторых случаях имя файла не является фактическим именем файла; например, во многих реализациях утилиты входа в систему используется соглашение о префиксах ('-') к фактическому имени файла, что указывает на то, что интерпретатор команд вызывается, что он является "оболочкой входа".
Также обратите внимание, что для тестирования и [утилит требуются определенные строки для аргумента argv [0] для детерминированного поведения во всех реализациях.
Может ли argc быть нулевым в системе POSIX?
Да, но это не будет строго соответствовать POSIX.
когда вы хотите запустить любой исполняемый файл, например ./a.out
него будет один аргумент, который program name
. Но в Linux можно запустить программу с argc
как zero
, выполнив ее из другой программы, которая вызывает execv
с empty argument list
.
например,
int main() {
char *buf[] = { NULL };
execv("./exe", buf); /* exe is binary which it run with 0 argument */
return 0;
}
TL; DR: Да, argv[0]
может быть NULL, но не для любой хорошей/разумной причины, о которой я знаю. Однако есть причины не беспокоиться, если argv[0]
является NULL, а также специально разрешить процессу сбой, если он есть.
Да, argv[0]
может быть NULL в системе POSIX, если и только если он был выполнен без каких-либо аргументов.
Более интересный практический вопрос заключается в том, должна ли ваша программа заботиться.
Ответ на этот вопрос: "Нет, ваша программа может предположить, что argv[0]
не является NULL", потому что некоторые системные утилиты (утилиты командной строки) либо не работают, либо работают недетерминированным образом, когда argv[0] == NULL
, но что более важно, нет веской причины (кроме глупости или гнусных целей), почему любой процесс будет делать это. (Я не уверен, что стандартное использование getopt()
также терпит неудачу, но я бы не ожидал, что он сработает.)
Много кода и, действительно, большинство примеров и утилит, которые я пишу, начинаются с эквивалента
int main(int argc, char *argv[])
{
if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
printf("Usage: %s [ -h | --help ]\n", argv[0]);
/* ... print usage ... */
return EXIT_SUCCESS;
}
и это разумно и приемлемо, потому что нет никакой веской причины для процесса выполнения другого процесса без предоставления хотя бы управляющего пути, то есть execlp(cmd, cmd, NULL)
а не execlp(cmd, NULL)
.
(Тем не менее, я могу придумать несколько подозрительных причин, например, использовать окна временной раскладки, связанные с командами pipe или socket: злой процесс отправляет злобный запрос через установленный сокет домена Unix, а затем немедленно заменяет собой уполномоченную команду жертвы (выполняется без каких-либо аргументов, чтобы обеспечить минимальное время запуска), так что, когда служба, получающая запрос, проверяет учетные данные для сверстников, она видит команду жертвы вместо первоначального злого процесса. Это, на мой взгляд, лучше для такой жертвы команды для жесткого и быстрого сбоя (SIGSEGV
, путем разыменования указателя NULL), а не пытаться и вести себя "красиво", давая злобному процессу большее окно времени.)
Другими словами, хотя процесс может заменить себя другим, но без каких-либо аргументов, приводящих к нулю argc
, такое поведение необоснованно, в строгом смысле, что нет никакой известной необоснованной причины этого.
Из-за этого и того факта, что я люблю делать жизнь тяжело для гнусных и беззаботных программистов и их программ, я лично никогда не добавлю тривиальный чек, аналогичный
static int usage(const char *argv0)
{
/* Print usage using argv0 as if it was argv[0] */
return EXIT_SUCCESS;
}
int main(int argc, char *argv[])
{
if (argc < 1)
return usage("(this)");
if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
return usage(argv[0]);
/* argv[0] and argv[1] are non-NULL, argc >= 2 */
за исключением случаев, когда этого требует кто-либо с конкретным существующим вариантом использования. И даже тогда я был бы немного подозрительным, желая сначала проверить прецедент.