TL;DR:是的,argv[0] 可以为 NULL,但不是出于我所知道的任何好的/理智的原因。但是,有理由不关心 argv[0] 是否为 NULL,并特别允许进程在它为空时崩溃。
是的,argv[0] 在 POSIX 系统上可以为 NULL,当且仅当它在没有任何参数的情况下执行。
更有趣的实际问题是,你的程序应该关心。
答案是“不,你的程序可以假设 argv[0] 不是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)。
(但是,我可以想到一些邪恶的原因,例如利用与管道或套接字命令相关的计时竞争窗口:一个邪恶的进程通过已建立的 Unix 域套接字发送一个邪恶的请求,然后立即用授权的受害者替换自己命令(不带任何参数运行,以确保最小的启动时间),这样当服务获取请求检查对等凭据时,它会看到受害者命令,而不是原始的邪恶进程。在我看来,这是最好的让这样的受害者命令快速崩溃(SIGSEGV,通过解除对 NULL 指针的引用),而不是尝试表现得“很好”,给邪恶进程一个更大的时间窗口。)
换句话说,虽然一个进程可能将自己替换为另一个进程但没有任何参数导致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 */
除非有特定现有用例的人提出要求。即使这样我也会有点怀疑,想先自己验证用例。