main()
在 C 或 C++ 应用程序中传递参数时,将argv[0]
始终是可执行文件的名称?或者这只是一个常见的约定,不能保证 100% 的时间是正确的?
8 回答
猜测(甚至是有根据的猜测)很有趣,但您确实需要查看标准文档才能确定。例如,ISO C11 声明(我的重点):
如果 的值
argc
大于零,则 指向的字符串argv[0]
代表程序名;argv[0][0]
如果主机环境中没有程序名,则应为空字符。
所以不,如果该名称可用,它只是程序名称。而它“代表”的是程序名,不一定是程序名。之前的部分指出:
如果 的值
argc
大于零,则argv[0]
通过argv[argc-1]
inclusive 的数组成员应包含指向字符串的指针,这些指针在程序启动之前由宿主环境赋予实现定义的值。
这与以前的标准 C99 没有变化,这意味着即使是值也不是由标准规定的——这完全取决于实现。
这意味着如果主机环境不提供程序名称,则程序名称可以为空,如果主机环境确实提供它,则程序名称可以为空,前提是“其他任何内容”以某种方式表示程序名称。在我更加虐待狂的时刻,我会考虑将其翻译成斯瓦希里语,通过替换密码运行它,然后以相反的字节顺序存储它:-)。
但是,实施定义在 ISO 标准中确实具有特定含义——实施必须记录其工作方式。argv[0]
因此,即使是 UNIX,它可以将它喜欢的任何东西放入exec
调用系列中,也必须(并且确实)记录它。
在带有调用的*nix
类型系统下,调用者在调用中放入的任何内容都将是。exec*()
argv[0]
argv0
exec*()
shell 使用程序名称的约定,大多数其他程序遵循相同的约定,因此argv[0]
通常是程序名称。
但是一个流氓 Unix 程序可以调用exec()
并制作argv[0]
它喜欢的任何东西,所以无论 C 标准怎么说,你都不能指望这 100% 的时间。
根据 C++ 标准,第 3.6.1 节:
argv[0] 应该是指向 NTMBS 的初始字符的指针,表示用于调用程序的名称或“”
所以不,它不能保证,至少标准是这样。
ISO-IEC 9899 规定:
5.1.2.2.1 程序启动
如果 的值
argc
大于零,则 指向的字符串argv[0]
代表程序名;argv[0][0]
如果主机环境中没有程序名,则应为空字符。如果 的值argc
大于 1,则argv[1]
through指向的字符串argv[argc-1]
表示程序参数。
我也用过:
#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) */
然后您只需解析字符串即可从路径中提取可执行文件名称。
具有argv[0] !=
可执行名称的应用程序
许多 shell 通过检查来确定它们是否是登录 shell
argv[0][0] == '-'
。登录 shell 具有不同的属性,特别是它们获取一些默认文件,例如/etc/profile
.它通常是 init 本身或
getty
添加前导-
,另请参见: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
可执行名称的可运行 POSIXexecve
示例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]
也可以是:
- NULL:argv[0] 什么时候可以为空?
- empty: argv[0] 可以包含一个空字符串吗?
在 Ubuntu 16.10 上测试。
我不确定它是几乎普遍的约定还是标准,但无论哪种方式,您都应该遵守它。不过,我从未见过它在 Unix 和类 Unix 系统之外被利用。在 Unix 环境中 - 尤其是在过去 - 程序可能具有显着不同的行为,具体取决于调用它们的名称。
已编辑:我同时从其他帖子中看到有人将其识别为来自特定标准,但我确信约定早于标准。
如果您通过 Workbench 启动 Amiga 程序,则不会设置 argv[0],只能通过 CLI 设置。