POSIX 系统上的 argc 可以为零吗?

Can argc be zero on a POSIX system?

给定主程序的标准定义:

int main(int argc, char *argv[]) {
   ...
}

在什么情况下 argc 在 POSIX 系统上可以为零?

是的,可以为零,也就是说argv[0] == NULL.

按照惯例,argv[0] 是程序的名称。 如果你自己启动二进制文件,你可以有 argc == 0,就像 execve 系列一样,不要给出任何参数。您甚至可以给出一个与程序名称相去甚远的字符串。这就是为什么使用 argv[0] 来获取程序名称并不完全可靠。

通常,shell 您键入命令行的地方总是将​​程序名称添加为第一个参数,但同样,这是一种约定。如果 argv[0] == "--help" 并且你使用 getopt 解析选项,你不会检测到它,因为 optind 被初始化为 1,但你可以设置 optind为 0,使用 getopt 和 "help" 长选项将显示。

长话短说:完全有可能 argc == 0argv[0] 本身并不是很特别)。当启动器根本不给出参数时就会发生这种情况。

是的,这是可能的。如果你调用你的程序如下:

execl("./myprog", NULL, (char *)NULL);

或者:

char *args[] = { NULL };
execv("./myprog", args);

那么在“myprog”中,argc将为0。

The standard 还特别允许 0 argc,如第 5.1.2.2.1 节中关于托管环境中的程序启动所述:

1 The function called at program startup is named main. The implementation declares no prototype for this function. It shall be defined with a return type of int and with no parameters:

int main(void) { /* ... */ } 

or with two parameters (referred to here as argc and argv, though any names may be used, as they are local to the function in which they are declared):

int main(int argc, char *argv[]) { /* ... */ }

or equivalent; or in some other implementation-defined manner.

2 If they are declared, the parameters to the main function shall obey the following constraints:

  • The value of argc shall be nonnegative.
  • argv[argc] shall be a null pointer.

...

另请注意,这意味着如果 argc 为 0,则 argv[0] 保证为 NULL。然而,标准中没有详细说明 printf 在用作 %s 说明符的参数时如何处理 NULL 指针。在这种情况下,许多实现会输出“(null)”,但不能保证。

Early proposals required that the value of argc passed to main() be "one or greater". This was driven by the same requirement in drafts of the ISO C standard. In fact, historical implementations have passed a value of zero when no arguments are supplied to the caller of the exec functions. This requirement was removed from the ISO C standard and subsequently removed from this volume of POSIX.1-2017 as well. The wording, in particular the use of the word should, requires a Strictly Conforming POSIX Application to pass at least one argument to the exec function, thus guaranteeing that argc be one or greater when invoked by such an application. In fact, this is good practice, since many existing applications reference argv[0] without first checking the value of argc.

The requirement on a Strictly Conforming POSIX Application also states that the value passed as the first argument be a filename string associated with the process being started. Although some existing applications pass a pathname rather than a filename string in some circumstances, a filename string is more generally useful, since the common usage of argv[0] is in printing diagnostics. In some cases the filename passed is not the actual filename of the file; for example, many implementations of the login utility use a convention of prefixing a ( '-' ) to the actual filename, which indicates to the command interpreter being invoked that it is a "login shell".

Also, note that the test and [ utilities require specific strings for the argv[0] argument to have deterministic behavior across all implementations.

Source


Can argc be zero on a POSIX system?

是的,但不会严格遵守 POSIX。

任何时候你想要运行任何像./a.out这样的可执行文件时,它都会有一个参数,那就是program name。但是可以 运行 argc 的程序作为 Linux 中的 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,但不是出于任何 good/sane 我知道的原因。但是,有理由不关心 argv[0] 是否为 NULL,如果是 NULL,则特别允许进程崩溃。


是的,argv[0] 在 POSIX 系统上可以为 NULL,当且仅当它是在没有任何参数的情况下执行的。

更有趣的实际问题是,你的程序应该关心

答案是 "No, your program can assume argv[0] is not 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).

(然而,我能想到一些邪恶的原因,比如利用与管道或套接字命令相关的时间竞争windows:一个邪恶的进程通过一个已建立的Unix域套接字发送一个邪恶的请求,然后立即用一个授权的受害者命令替换自己(运行 没有任何参数,以确保最短的启动时间),这样当收到请求的服务检查对等凭据时,它会看到受害者命令,而不是原来的恶意命令process. 在我看来,最好让这样的受害者命令快速崩溃(SIGSEGV,通过取消引用 NULL 指针),而不是尝试和表现 "nicely",给邪恶的进程更大的时间 window.)

换句话说,虽然可能一个进程用另一个进程替换自己但没有任何参数导致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 */

除非考虑到特定现有用例的人提出请求。即使那样我也会有点怀疑,想先自己验证用例。

为了补充其他答案,C 中没有任何内容(POSIX 或没有)阻止 main() 在程序中作为函数被调用。

int main(int argc, int argv[]) {
    if (argc == 0) printf("Hey!\n");
    else main(0,NULL);

    return 0;
}