Когда argv [0] имеет значение null?

То, что я понял о передаче аргументов main() из командной строки, состоит в том, что argc имеет минимальное значение 1, а argv [0] всегда будет иметь имя программы с его контуром в нем.

Если аргументы предоставлены в командной строке, то argc будет иметь значение больше единицы, а argv 1 - argv [argc-1] будет иметь эти аргументы.

Теперь параграф по этой ссылке говорит, что

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

Теперь, как и когда argv [0] имеет пустую строку? Я имею в виду, что имя программы со своим путем всегда будет доступно, если оно может быть нулевым?

Писатель говорит, что "если это не доступно", но когда и как возможно, что имя программы недоступно?

Ответ 1

С классом вызовов exec вы указываете имя программы и исполняемый файл отдельно, чтобы затем установить значение NULL.

Но эта цитата на самом деле соответствует стандарту ISO (возможно, перефразирована), и этот стандарт охватывает очень большой диапазон окружений исполнения от самого маленького микроконтроллера до последнего мэйнфрейма z10 Enterprise-класса.

Многие из этих встроенных систем были бы в ситуации, когда исполняемое имя имеет мало смысла.

Из последней версии c1x:

Значение argc должно быть неотрицательным.

Значение argv[argc] должно быть нулевым указателем.

Если значение argc больше нуля, элементы массива argv[0] через argv[argc-1] включительно должны содержать указатели на строки, которым перед запуском программы заданы значения, заданные реализацией.

Это означает, что если argc равно нулю (и оно может быть), argv [0] равно NULL.

Но даже если argc не равно 0, вы не можете получить имя программы, так как в стандарте также указано:

Если значение argc больше нуля, строка, на которую указывает argv[0], представляет собой имя программы; argv[0][0] должен быть нулевым символом, если имя программы недоступно в среде хоста. Если значение argc больше единицы, строки, на которые указывает argv[1] через argv[argc-1], представляют программные параметры.

Таким образом, в стандарте нет требования о предоставлении имени программы. Я видел, что программы используют широкий выбор параметров для этого значения:

  • Нет значения вообще (для предполагаемой безопасности).
  • вопиющая ложь (например, sleep для вредоносного кода).
  • фактическое имя программы (например, sleep).
  • слегка измененный (например, -ksh для оболочки входа).
  • описательное имя (например, progname - a program for something).

Ответ 2

Согласно этому списку рассылки argv[0] может быть нулевым, если argc == 0. Но они не объясняют, когда argc может когда-либо быть нулевым. Я бы предположил, что argc будет равен нулю в ситуациях, когда исполняемый файл не запускался "нормально" (т.е. Через командную строку, popen и т.д.) - и действительно, как упоминалось в @paxdiablo, вы можете вручную установить argv с семейством exec функции, поэтому argc может быть нулевым в зависимости от этих аргументов.

Но в разделе "Обоснование":

Ранним предложениям требовалось, чтобы значение argc перешло в main() как "одно или большее". Это было обусловлено тем же требованием в проектах стандарта ISO C. Фактически, исторические реализации передали значение нуля, когда аргументы exec не передают аргументы. Это требование было удалено из стандарта ISO C и впоследствии удалено из этого объема IEEE Std 1003.1-2001. Формулировка, в частности использование слова, требует, чтобы строго соответствующее приложение POSIX передавало по крайней мере один аргумент функции exec, тем самым гарантируя, что argc будет одним или большим при вызове такого приложения. На самом деле это хорошая практика, поскольку многие существующие приложения ссылаются на argv[0] не проверяя сначала значение argc.

Итак, у вас есть это: строгое соответствие приложениям POSIX должно иметь значение argc больше нуля, но это отнюдь не гарантировано.

Там немного больше информации о стандарте относительно argc и argv в разделе " Запуск программы ".

Ответ 3

Пример запуска POSIX argv[0] == NULL

caller.c

#define _XOPEN_SOURCE 700
#include <unistd.h>

int main(void) {
    char *argv[] = {NULL};
    char *envp[] = {NULL};
    execve("callee.out", argv, envp);
}

callee.c

#include <stdio.h>

int main(int argc, char **argv) {
    if (argc == 0 && argv[0] == NULL)
        puts("yup");
}

Тогда:

gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o caller.out caller.c
gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o callee.out callee.c
./caller.out

Выход:

yup

Тестировать существующую программу с пустым списком аргументов

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

Вызывающий-any.c

#include <unistd.h>
#include <stdio.h>

int main(int argc, char**argv) {
    char *empty[] = {NULL};
    execve(argv[1], empty, empty);

}

Пример использования:

./caller-any.out /bin/ls

Инструменты GNU Coreutils, такие как ls, однако, имеют проверку на argv[0] NULL, как упомянуто в: Почему системный вызов execve может выполнить "/bin/sh " без аргументов argv, но без выводов "/bin/ls"? и ls:

A NULL argv[0] was passed through an exec system call.
Aborted (core dumped)

Проверено в Ubuntu 19.04.

Ответ 4

Можно представить себе платформы, где программы не имеют имен - возможно, код просто загружается при запуске. На тех, argv [0] я мог догадаться, что NULL. Стандарт C, безусловно, допускает значение argc, равное нулю, и говорит, что argv [argc] должен быть NULL.