33

我对从命令行向 main() 传递参数的理解是 argc 的最小值为 1 并且 argv[0] 将始终包含程序名称及其路径。

如果在命令行中提供了参数,则 argc 将具有大于 1 的值,并且 argv 1到 argv[argc-1] 将具有这些参数。

现在这个链接上的一段说

argv[0] 将是一个包含程序名称的字符串,如果不可用,则为空字符串。

现在,argv[0] 如何以及何时可以有空字符串?我的意思是程序名称及其路径将始终可用,那么它什么时候可以为空?

作家说“如果那不可用”,但是程序名称何时以及如何可能不可用?

4

4 回答 4

33

使用exec调用类,您可以分别指定程序名称和程序可执行文件,以便您可以将其设置为 NULL。

但该引述实际上来自 ISO 标准(可能是释义),该标准涵盖了从最小的微控制器到最新的 z10 企业级大型机的非常大范围的执行环境。

许多这些嵌入式系统将处于可执行名称没有意义的情况。

来自最新的 c1x 草案:

的值argc应为非负数。

该值argv[argc]应为空指针。

如果 的值argc大于零,则argv[0]通过argv[argc-1]inclusive 的数组成员应包含指向字符串的指针,这些指针在程序启动之前由宿主环境赋予实现定义的值。

这意味着,如果argc为零(它可以是),则 argv[0] 为 NULL。

但是,即使argc不是0,您也可能无法获得程序名称,因为标准还规定:

如果 的值argc大于零,则 指向的字符串argv[0]代表程序名;argv[0][0]如果主机环境中没有程序名,则应为空字符。如果 的值argc大于 1,则argv[1]through指向的字符串argv[argc-1]表示程序参数。

因此,该标准没有要求提供程序名称。我已经看到程序为此值使用了多种选项:

  • 根本没有价值(对于假定的安全性)。
  • 公然的谎言(例如sleep恶意代码)。
  • 实际的程序名称(例如sleep)。
  • 稍作修改的(例如-ksh用于登录 shell)。
  • 描述性名称(例如,progname - a program for something)。
于 2010-05-08T13:08:36.757 回答
7

根据这个邮件列表argv[0]如果argc == 0. 但他们没有解释什么时候 argc可能会为零。我怀疑 argc在未“正常”启动可执行文件(即通过命令行popen等)的情况下为零 - 事实上,正如@paxdiablo 提到的,您可以手动设置argv函数exec系列,所以argc可以根据这些论点为零。

但是,在他们的基本原理部分:

早期的提议要求argc传递的值main()是“一个或更大”。这是由 ISO C 标准草案中的相同要求推动的。事实上,当没有参数提供给 exec 函数的调用者时,历史实现已经传递了一个零值。此要求已从 ISO C 标准中删除,随后也从本卷 IEEE Std 1003.1-2001 中删除。措辞,特别是单词 should 的使用,要求严格符合 POSIX 应用程序将至少一个参数传递给 exec 函数,从而保证argc在此类应用程序调用时为一个或更大。事实上,这是一种很好的做法,因为许多现有应用程序在引用argv[0]时没有先检查argc.

所以你有它:严格符合 POSIX 应用程序必须argc大于零,但这绝不是保证。

程序启动部分有更多关于标准的信息argcargv

于 2010-05-08T13:05:35.117 回答
6

可运行的 POSIX 示例argv[0] == NULL

调用者.c

#define _XOPEN_SOURCE 700
#include <unistd.h>

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

被调用者.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 系统调用可以在没有任何 argv 参数的情况下运行“/bin/sh”,而不是“/bin/ls”?ls输出:

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

在 Ubuntu 19.04 中测试。

于 2017-02-17T06:28:54.413 回答
3

可以想象程序没有名称的平台 - 也许代码只是在启动时加载。在那些上,我猜 argv[0] 可能是 NULL。C 标准当然允许 argc 值为零,并说 argv[argc] 应为 NULL。

于 2010-05-08T13:10:17.397 回答