鉴于主程序的标准定义:
int main(int argc, char *argv[]) {
...
}
在POSIX系统中,argc
在哪种情况下可以为零?
答案 0 :(得分:89)
是的,有可能。如果您按以下方式致电您的计划:
execl("./myprog", NULL, (char *)NULL);
或者替代:
char *args[] = { NULL };
execv("./myprog", args);
然后在“myprog”中,argc
将为0。
The standard还特别允许0 argc
,如第5.1.2.2.1节所述,关于托管环境中的程序启动:
1 程序启动时调用的函数名为
main
。该实现声明此函数没有原型。应该是 使用返回类型int
定义并且没有参数:int main(void) { /* ... */ }
或有两个参数(此处称为
argc
和argv
,但可以使用任何名称,因为它们是本地的 到它们被声明的函数):int main(int argc, char *argv[]) { /* ... */ }
或同等的;或者以其他一些实现定义的方式。
2 如果声明它们,
main
函数的参数应遵守以下约束条件:
argc
的值应为非负值。argv[argc]
应为空指针。...
另请注意,这意味着如果argc
为0,则argv[0]
保证为NULL。 printf
如何在用作%s
说明符的参数时处理NULL指针,但标准中并未详细说明。在这种情况下,许多实现将输出“(null)”,但我不相信它是有保证的。
答案 1 :(得分:22)
要添加到其他答案,C(POSIX与否)中没有任何内容阻止main()作为程序中的函数被调用。
int main(int argc, int argv[]) {
if (argc == 0) printf("Hey!\n");
else main(0,NULL);
return 0;
}
答案 2 :(得分:19)
是的,它可以为零,意味着argv[0] == NULL
。
argv[0]
是程序的名称。
如果您自己启动二进制文件,则可以使用argc == 0
,例如使用execve系列,并且不提供任何参数。您甚至可以提供一个远不是程序名称的字符串。这就是使用argv[0]
来获取程序名称的原因并不完全可靠。
通常,键入命令行的shell总是将程序名称添加为第一个参数,但同样,它是一种约定。如果argv[0]
==“ - help”并且您使用getopt来解析选项,则无法检测到它,因为optind
已初始化为1,但您可以设置optind
为0,使用getopt
和“帮助”长选项将显示。
长话短说:argc == 0
完全可能(argv[0]
本身并不特别)。当发射器根本没有给出参数时就会发生这种情况。
答案 3 :(得分:15)
早期的提案要求传递给main()的argc值为 "一个或多个"。这是由草案中的相同要求驱动的 ISO C标准。事实上,历史实施已经过去了 当没有向exec的调用者提供参数时,值为零 功能。此要求已从ISO C标准中删除 随后从POSIX.1-2017的这一卷中删除。该 措辞,特别是使用“应该”这个词,需要严格要求 符合POSIX应用程序以向exec传递至少一个参数 函数,从而保证在调用时argc为1或更大 通过这样的申请。事实上,这是很好的做法,因为很多 现有的应用程序参考argv [0]而不先检查 argc的价值。
严格遵守POSIX应用程序的要求也说明 作为第一个参数传递的值是文件名字符串 与正在启动的进程相关联。一些现有的 应用程序传递路径名而不是某些文件名字符串 在某些情况下,文件名字符串通常更有用,因为 argv [0]的常见用法是打印诊断。在某些情况下 传递的文件名不是文件的实际文件名;例如, 登录实用程序的许多实现都使用约定 将(' - ')添加到实际文件名的前缀,即 指示调用命令解释器它是一个"登录 壳"
另外,请注意测试和[实用程序需要特定的字符串 argv [0]参数在所有方面都具有确定性行为 的实施方式。
POSIX系统上argc可以为零吗?
是的,但不会严格符合POSIX。
答案 4 :(得分:3)
每当你想运行像./a.out
这样的任何可执行文件时,它都会有一个program name
的参数。但是,可以在Linux中运行argc
为zero
的程序,方法是从另一个使用execv
调用empty argument list
的程序执行该程序。
例如
int main() {
char *buf[] = { NULL };
execv("./exe", buf); /* exe is binary which it run with 0 argument */
return 0;
}
答案 5 :(得分:3)
TL; DR:是的,argv[0]
可以为NULL,但不是出于我所知道的任何好/理智的原因。但是,有理由不关心argv[0]
是否为NULL,并且特别允许进程崩溃。
是的,argv[0]
在POSIX系统上可以为NULL,当且仅当它是在没有任何参数的情况下执行时。
更有趣的实际问题是,你的程序应该关注。
答案就是"不,你的程序可以假设 argv[0]
不是NULL" ,因为有些系统工具(命令) -line utilities)要么在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)
而不是{{ 1}}。
(但是,我可以想到一些邪恶的原因,比如利用与管道或套接字命令相关的计时竞赛窗口:恶意进程通过已建立的Unix域套接字发送恶意请求,然后立即用授权的受害者替换自己命令(没有任何参数运行,以确保最短的启动时间),以便当获取请求的服务检查对等凭证时,它会看到受害者命令,而不是原始恶意进程。在我看来,这是最好的对于这样的受害者命令,通过解除引用NULL指针来快速崩溃(execlp(cmd, NULL)
),而不是尝试和表现良好&#34;给邪恶进程一个更大的时间窗口。)
换句话说,虽然可能是一个进程用另一个进程替换自己但没有任何参数导致SIGSEGV
为零,但这种行为是不合理的,严格意义上说没有已知的非邪恶的理由这样做。
正因为如此,以及我喜欢为邪恶和无情的程序员及其程序而努力工作的事实,我个人永远不会添加琐碎的支票,类似于
argc
除非有特定现有用例的人要求,否则除外。即便如此,我还是有点怀疑,想先自己验证用例。