argv[0]可以包含空字符串吗?

9
在任何C程序中,命令行参数argv [0]指向用于调用程序的名称。是否存在某种情况,它将指向空字符串“”
如果有这样的情况,以下是一个代码片段的示例参考:

1
为什么不设计你的程序,使其无论如何都能正常工作呢?或者使用运行时断言。 - tangrs
1
最好称为空字符串;空字符串可以太容易地与空指针混淆。 - Jonathan Leffler
相关:NULL:https://dev59.com/questions/ZXE85IYBdhLWcg3wZyqt#42290873 - Ciro Santilli OurBigBook.com
4个回答

9
它是实现定义的。 §5.1.2.2.1 简述:
- 如果argc 的值大于零,则数组成员 argv[0]argv[argc - 1](包括这两个元素)必须包含指向字符串的指针,这些指针在程序启动前由主机环境赋予实现定义的值。其目的是从托管环境中的其他位置提供信息以供程序在程序启动之前确定。 - 如果argc 的值大于零,则由argv [0] 指向的字符串表示程序名称;如果程序名称在主机环境中不可用,则argv[0][0] 应为空字符。
因此,如果 argc 大于零,则argv[0] 绝不会是空字符串,但这种情况可能会发生。(请注意,当 argc 等于 n 时,argv[0]argv[n - 1] 永远不为 null,总是指向一个字符串。该字符串本身可能为空。如果 n 为零,则 argv[0] 为 null。)
实际上,您只需要确保所针对的平台按预期行事即可。

实际上,如果 argv[0][0] 可能为 0,则 argv[0] 可能为空。 - alk
@alk:当argc大于零时,argv[0]永远不会为空,但它可能指向一个空字符串。 - GManNickG
@KeithThompson:你说了算...;-) - alk
@GMan 感谢您的回答。请问您从哪里获取C规范?例如,您从哪里获取有关5.1.2.2.1的信息? - Sangeeth Saravanaraj
2
@SangeethSaravanaraj:这里是C99标准的最新草案。这里是最新的可用草案,涵盖了2011年发布的标准。 - Keith Thompson

7
是的。
C语言标准明确允许argv[0]可能为空指针,或者可能指向空字符串("")。N1256 5.1.2.2.1p2:
引用:

argc的值必须为非负数。

argv[argc]必须是空指针。

[...]

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

在类Unix系统上,程序是通过exec()函数族(execl()execlp()等)之一来调用的,调用者可以指定传递给main()函数的确切参数。(甚至可以以违反C标准规定的方式调用程序。)
请注意,标准规定argv[0](假设它既不是null也不是空)"表示程序名称"。标准故意模糊关于如何表示程序名称的细节。特别是,它不需要提供可用于调用程序的名称(因为标准甚至不要求可以通过名称调用程序)。

1
它不能为null,只能是空的。前面的段落要求每个argv指向一个字符串(最后一个必须为null)。一个糟糕的实现可能会将它们全部提供为空字符串,但不是全部为null。 - GManNickG
如果 argc == 0,那么 argv[0] 是一个空指针。(当您写下您的评论时,我没有引用那部分。) - Keith Thompson
这并不是重点。不过我已经点赞了,因为我们的答案很相似。 - GManNickG
@KeithThompson +1提到了exec()函数族!谢谢!! - Sangeeth Saravanaraj
那么我们是否可以简单地假设exec的调用者按照标准来调用它,忽略程序漏洞呢,@KeithThompson? - user129393192
显示剩余5条评论

7
其他回复已经引用了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可以调用其他程序,并允许用户指定该程序的命令行参数。
#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 非常棒的例子!阅读您的帖子确实很有趣!谢谢 :) - Sangeeth Saravanaraj

1

在C语言中,argv [ 0 ]可以为空,例如如果您直接调用主函数(在C中可以使用一些技巧来实现)。 我不知道C ++是否允许直接调用主函数。


OP是不是在问argv[0]是否指向一个空字符串("", {'\0'}),而不是指向NULL - alk
@alk,这个问题是关于空/空字符串的,而不是argv[0]=NULL - Sangeeth Saravanaraj
C++ 不允许你调用 main() 函数;而 C 语言可以。 - Jonathan Leffler
如果可以为空,则返回已翻译的文本:答案的核心是“当系统未调用主函数时”。例如?你可以在C中编写一个调用自身的递归主函数而不传递argv的示例。在C中,main只是一个函数。但是我认为直接调用main是非常糟糕的做法。但我们不是在谈论好坏实践吗?有关详细信息,请参见GMan和Keith的答案。他们比我的更好(他们实际上给出了引用)。 - BigMike
1
是的,在C语言中,如果你递归调用main函数,可以传递任意垃圾值。类似于argc == 2, argv[1] == NULL这样的情况很可能破坏正常的参数处理。但这只是你代码中的一个错误。在最初调用main函数时,你可以安全地做出关于argcargv的假设。 - Keith Thompson
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接