【问题标题】:Can argv[0] contain an empty string?argv[0] 可以包含一个空字符串吗?
【发布时间】:2011-12-29 08:19:56
【问题描述】:

在任何 C 程序中,命令行参数argv[0] 指向用于调用程序的名称。在任何情况下它会指向一个空字符串""

这种情况的示例代码 sn-p 将是一个很好的参考。

【问题讨论】:

标签: c argv


【解决方案1】:

它是定义的实现。 §5.1.2.2.1 节略:

  • 如果argc的值大于零,则数组成员argv[0]通过 argv[argc-1] inclusive 应包含指向字符串的指针,这些指针是给定的 在程序启动之前由主机环境实现定义的值。这 意图是向程序提供在程序启动之前确定的信息 来自托管环境中的其他地方。 [...]

  • 如果argc的值大于零,则argv[0]指向的字符串 代表程序名称; argv[0][0] 应为空字符,如果 程序名称在主机环境中不可用。 [...]

因此,如果argc 大于零,则意图argv[0] 永远不会是空字符串,但它可能会发生。 (请注意,当argc 等于n 时,argv[0]argv[n - 1] 永远不会为空,并且始终指向一个字符串。不过,字符串本身可能为空。如果n 为零,则argv[0] 为空。)

当然,在实践中,您只需要确保您的目标平台按需要运行即可。

【讨论】:

  • 所以事实上如果 argv[0][0] 可能是 0 argv[0] 可能'是'空的。
  • @alk: 当argc 大于零时,argv[0] 永远不会为空,但它可能指向一个空字符串。
  • @KeithThompson:你的名字...... ;-)
  • @GMan 感谢您的回答。你能告诉我你从哪里得到C规范吗? f.e.您从哪里获得有关 5.1.2.2.1 的信息?
  • @SangeethSaravaraj:This 是 C99 标准的最新草案。 This 是最近发布的 2011 标准的最新可用草案。
【解决方案2】:

是的。

C 语言标准明确允许argv[0] 可以是空指针,它可以指向空字符串("")。 N1256 5.1.2.2.1p2:

argc 的值应为非负数。

argv[argc] 应为空指针。

[...]

如果argc的值大于零,则指向的字符串 argv[0]代表程序名argv[0][0] 应为 如果程序名称不能从主机获得,则为空字符 环境。如果 argc 的值大于 1,则字符串 argv[1]argv[argc-1] 所指向的表示 程序参数

在类 Unix 系统上,程序由 exec() 系列函数之一(execl()execlp() 等)调用,它允许调用者准确指定传递给 @987654326 的参数@ 功能。 (甚至可能以违反 C 标准要求的方式调用程序。)

请注意,标准规定 argv[0](假设它既不是 null 也不是空的)“表示程序名称”。该标准故意模糊 如何 表示程序名称。特别是,它不需要提供可以调用程序的名称(因为标准甚至不要求程序可以通过名称调用)。

【讨论】:

  • 不能为空,只能为空。上一段要求每个argv 指向一个字符串(并且最后一个为空)。一个蹩脚的实现可以为它们都提供空字符串,但不是全部为空。
  • 如果argc == 0,那么argv[0]是一个空指针。 (当你写你的评论时,我没有引用那部分。)
  • 不是。不过,我投了赞成票,因为我们的答案很相似。
  • @KeithThompson +1 提到了exec() 系列函数!谢谢!!
【解决方案3】:

其他回复引用了C标准并显示argv[0]可以是NULL也可以是空字符串("")。您应该假设可能发生这种情况来编写程序,否则您将产生(小)安全风险。很容易调用您的程序并将argv 设置为攻击者想要的任何内容。作为证明,请考虑以下两个程序。第一个,echoargv.c打印出argv的内容:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {
    int i;
    for (i = 0; i < argc; ++i)
        printf("argv[%d] = \"%s\"\n", i, argv[i]);
    exit(0);
}

第二个,argv0,调用任何其他程序并让用户指定其他程序的 argv:

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

int main(int argc, char **argv) {
    (void) execv(argv[1], argv+2);
    perror("execv");
    exit(1);
}

(这是特定于 Posix 的版本。非标准环境可能需要更改。)

以下是它们的使用方法:

$ gcc -o echoargv echoargv.c 
$ gcc -o argv0 argv0.c 
$ ./argv0 ./echoargv 
$ ./argv0 ./echoargv ''
argv[0] = ""
$ ./argv0 ./echoargv 'this is fun' 'it is fun indeed'
argv[0] = "this is fun"
argv[1] = "it is fun indeed"
$ 

argv0 的第一次运行将 echoargvargv[0] 设置为 NULL。 第二次运行使其成为空字符串。 第三次运行只是为了好玩:注意argv[0] 不需要 与程序的实际名称有关。

这怎么会咬你?例如,如果您在使用信息中盲目地打印出您的程序名称:

printf("usage: %s [options] [FILE]...\n", argv[0]);

更好:

const char *program_name = "some default name"; /* (global) variable */
if (argv[0] && argv[0][0])
    program_name = argv[0];
printf("usage: %s [options] [FILE]...\n", program_name);

如果您不这样做,攻击者可能会导致您的程序随意出现段错误,或者可能让您的程序向用户报告完全错误的内容。

【讨论】:

  • +1 为真棒示例!读你的帖子确实很有趣!!谢谢:)
【解决方案4】:

argv[0] 在 C 中可以为 null,例如,如果您直接调用 main 函数(可以在 C 中完成一些技巧)。我不知道 C++ 是否允许直接 main 调用。

【讨论】:

  • OP 不是问argv[0] 指的是一个空字符串(""{'\0'}),而是argv[0]NULL
  • @alk OP 是关于空/空字符串而不是argv[0]=NULL
  • C++ 不允许你调用main(); C 可以。
  • 如果可以为空,那么也可以为空 (\0)。答案的核心是“系统未调用 main 时”。一个例子 ?好吧,您可以在 C 中编写一个 main 递归调用自身而无需传递 argv。在 C 中 main 只是一个函数。但是我认为直接调用 main 是非常糟糕的坏习惯。但我们不是在谈论好的或坏的做法,不是吗?有关详细信息,请参阅 GMan 和 Keith 的答案。他们比我的好(他们实际上给了参考)。
  • @BigMike +1 提到了对main() 的直接调用。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-10
相关资源
最近更新 更多