在C或C ++应用程序中将参数传递给main()
时,argv[0]
是否始终是可执行文件的名称?或者这只是一个常见的约定,并不保证100%的时间都是真的?
答案 0 :(得分:103)
猜测(即使是有教育意义的猜测)也很有趣,但你确实需要去标准文件。例如,ISO C11陈述(我的重点):
如果
argc
的值大于零,则argv[0]
指向的字符串代表 程序名称;如果程序名不能从主机环境获得,则argv[0][0]
应为空字符。
所以不,它只是程序名称,如果该名称可用。它“代表”程序名称,不一定是程序名称。之前的部分说明:
如果
argc
的值大于零,则数组成员argv[0]
到argv[argc-1]
包含指向字符串的指针,这些指针由主机环境事先给出实现定义的值编程启动。
这与之前的标准C99没有变化,这意味着即使值也不是由标准决定的 - 它完全取决于实现。
这意味着如果主机环境不提供它,程序名称可以为空,如果主机环境 提供它,则其他任何内容,前提是“其他任何“以某种方式代表程序名称。在我更悲惨的时刻,我会考虑将其翻译成斯瓦希里语,通过替换密码运行它,然后以反向字节顺序存储它: - )。
但是,实现定义的确实在ISO标准中具有特定含义 - 实现必须记录其工作原理。因此,即使是UNIX,它可以将任何喜欢的内容放入argv[0]
exec
系列调用中,也必须(并且确实)记录它。
答案 1 :(得分:45)
在*nix
类型系统中,exec*()
次呼叫,argv[0]
将是呼叫者在argv0
呼叫中放入exec*()
点的任何内容。
shell使用这是程序名称的约定,大多数其他程序遵循相同的约定,因此argv[0]
通常是程序名。
但是一个流氓Unix程序可以调用exec()
并使argv[0]
任意一点,所以无论C标准是什么,你都不能指望100%的时间。
答案 2 :(得分:8)
根据C ++标准,第3.6.1节:
argv [0]应该是指针 NTMBS的初始特征 表示用于调用的名称 程序或“”
所以不,至少在标准方面,它无法得到保证。
答案 3 :(得分:4)
元素argv [0]通常包含程序的名称,但不应该依赖它 - 无论如何,程序不知道自己的名字是不常见的!
然而,其他页面似乎支持这样一个事实:它始终是可执行文件的名称。 This one州:
您会注意到argv [0]是程序本身的路径和名称。这允许程序发现有关自身的信息。它还为程序参数数组添加了一个,因此在获取命令行参数时常见的错误是在需要argv [1]时获取argv [0]。
答案 4 :(得分:4)
ISO-IEC 9899声明:
5.1.2.2.1程序启动
如果
argc
的值大于零,则argv[0]
指向的字符串代表程序名;如果程序名称在主机环境中不可用,则argv[0][0]
应为空字符。如果argc
的值大于1,则argv[1]
指向的字符串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) */
然后你只需要解析字符串以从路径中提取可执行文件名。
答案 5 :(得分:3)
拥有argv[0] !=
可执行文件名
许多shell通过选中argv[0][0] == '-'
来确定它们是否是登录shell。登录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
可运行的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);
}
b.c
#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上测试。
答案 6 :(得分:2)
我不确定它是否是一个几乎普遍的惯例或标准,但无论哪种方式你都应该遵守它。不过,我从未见过它在Unix和类Unix系统之外被利用。在Unix环境中 - 也许特别是在过去 - 程序可能具有明显不同的行为,具体取决于调用它们的名称。
编辑:我在同一时间从其他帖子中看到有人认为它来自特定标准,但我确信该惯例早于标准。
答案 7 :(得分:0)
如果通过Workbench argv [0]启动Amiga程序,则仅通过CLI不会设置。