“ argv [0] = 名称的可执行文件”是一个公认的标准还是仅仅是一个公共约定?

在 C 或 C + + 应用程序中向 main()传递参数时,argv[0]是否总是可执行文件的名称?或者这只是一个普通的惯例,并不能保证100% 的时间都是正确的?

81779 次浏览

本页 说明:

元素 argv [0]通常包含程序的名称,但是不应该依赖这个名称——无论如何,程序不知道自己的名称是很不寻常的!

但是,其他页面似乎支持这样一个事实,即它始终是可执行文件的名称:

您会注意到 argv [0]是程序本身的路径和名称。这允许程序发现关于自身的信息。它还向程序参数数组添加了另一个参数,因此在获取命令行参数时常见的错误是在需要 argv [1]时抓取 argv [0]。

根据 C + + 标准,第3.6.1节:

Argv [0]应该是指向 非本地住宅按揭证券的首字母 表示用于调用 program or ""

So no, it is not guaranteed, at least by the Standard.

猜测工作(甚至是受过教育的猜测工作)很有趣,但是您确实需要查看标准文档来确认。例如,ISOC11规定(我的重点) :

如果 argc的值大于零,则 argv[0]指向的字符串 代表为程序名; 如果程序名不能从主机环境中获得,则 argv[0][0]为空字符。

所以不,只有程序名称是 有空。,程序名称是 “代表”,程序名称不一定是 is。之前的一节说明:

如果 argc的值大于零,那么包括 argv[0]argv[argc-1]在内的数组成员应该包含指向字符串的指针,这些字符串在程序启动之前由主机环境提供实现定义的值。

这与之前的标准 C99没有改变,这意味着即使是 价值观也不是由标准决定的——它完全取决于实现。

这意味着如果宿主环境 没有提供了程序名,那么程序名可以是空的,如果宿主环境 是的提供了程序名,那么其他任何程序名都可以是空的,只要“其他任何程序名”以某种方式表示程序名。在我比较虐待狂的时候,我会考虑把它翻译成斯瓦希里语,通过一个替换式密码运行它,然后以相反的字节顺序存储它: ——)。

然而,实现定义的 does在 ISO 标准中有特定的含义——实现必须记录它是如何工作的。因此,即使 UNIX 可以将它喜欢的任何内容放入具有 exec系列调用的 argv[0]中,它也必须(而且确实)对其进行文档化。

在具有 exec*()调用的 *nix类型系统中,argv[0]将是调用者在 exec*()调用中放入 argv0点的任何东西。

Shell 使用的约定是这是程序名,而大多数其他程序遵循相同的约定,所以 argv[0]通常是程序名。

但是,一个流氓 Unix 程序可以调用 exec(),并使 argv[0]成为它喜欢的任何东西,所以不管 C 标准说什么,你不能指望这100% 的时间。

ISO-IEC9899规定:

5.1.2.2.1程序启动

If the value of argc is greater than zero, the string pointed to by argv[0] represents the programname; argv[0][0] shall be the null character if the program name is not available from the host environment. If the value of argc is greater than one, the strings pointed to by argv[0]0 through argv[0]1 represent the argv[0]2.

我还用过:

#if defined(_WIN32)
static size_t getExecutablePathName(char* pathName, size_t pathNameCapacity)
{
return GetModuleFileNameA(NULL, pathName, (DWORD)pathNameCapacity);
}
#elif defined(__linux__) /* elif of: #if defined(_WIN32) */
#include <unistd.h>
static size_t getExecutablePathName(char* pathName, size_t pathNameCapacity)
{
size_t pathNameSize = readlink("/proc/self/exe", pathName, pathNameCapacity - 1);
pathName[pathNameSize] = '\0';
return pathNameSize;
}
#elif defined(__APPLE__) /* elif of: #elif defined(__linux__) */
#include <mach-o/dyld.h>
static size_t getExecutablePathName(char* pathName, size_t pathNameCapacity)
{
uint32_t pathNameSize = 0;


_NSGetExecutablePath(NULL, &pathNameSize);


if (pathNameSize > pathNameCapacity)
pathNameSize = pathNameCapacity;


if (!_NSGetExecutablePath(pathName, &pathNameSize))
{
char real[PATH_MAX];


if (realpath(pathName, real) != NULL)
{
pathNameSize = strlen(real);
strncpy(pathName, real, pathNameSize);
}


return pathNameSize;
}


return 0;
}
#else /* else of: #elif defined(__APPLE__) */
#error provide your own implementation
#endif /* end of: #if defined(_WIN32) */

然后您只需要解析字符串,从路径中提取可执行文件名。

我不确定这是一个近乎普遍的惯例还是一个标准,但不管怎样,你都应该遵守它。不过,我从未见过它在 Unix 和类 Unix 系统之外被利用。在 Unix 环境中——尤其是在过去——程序可能会根据调用它们的名称有显著不同的行为。

EDITED: I see from other posts at the same time as mine that someone has identified it as coming from a particular standard, but I'm sure the convention long predates the standard.

具有 argv[0] !=可执行名称的应用程序

  • 许多 shell 通过检查 argv[0][0] == '-'来确定它们是否是登录 shell。登录 shell 有不同的属性,值得注意的是,它们源代码一些默认文件,如 /etc/profile

    通常是 init 本身或 getty添加了前导 -,参见: < a href = “ https://unix.stackexchange.com/questions/299408/how-to-login-auto-without-type-the-root-username-or-password-in-build/300152 # 300152”> https://unix.stackexchange.com/questions/299408/how-to-login-automatically-without-typing-the-root-username-or-password-in-build/300152#300152

  • 多调用二进制文件,可能最明显的是 Busybox。这些符号链接多个名称,例如 /bin/sh/bin/ls到一个可执行 /bin/busybox,它识别从 argv[0]使用哪个工具。

    这使得有可能拥有一个表示多个工具的小型静态链接可执行文件,并且基本上可以在任何 Linux 环境上工作。

参见: https://unix.stackexchange.com/questions/315812/why-does-argv-include-the-program-name/315817

可运行的 POSIX execve示例,其中 argv[0] !=可执行文件名

其他人提到了 exec,但这里是一个可运行的示例。

空调

#define _XOPEN_SOURCE 700
#include <unistd.h>


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

公元前

#include <stdio.h>


int main(int argc, char **argv) {
puts(argv[0]);
}

然后:

gcc a.c -o a.out
gcc b.c -o b.out
./a.out

给予:

yada yada

是的,argv[0]也可以是:

在 Ubuntu 16.10上测试。

如果使用 Workbench argv [0]启动 Amiga 程序,则不会设置它,只能使用 CLI 设置。