在C或C++应用程序中向main()传递参数时,argv[0]是否始终是可执行文件的名称?或者这只是一个常见的约定,并不能保证100%的时间都是正确的?
main()
argv[0]
yc0p9oo01#
猜测(即使是有根据的猜测)很有趣,但您确实需要查看标准文档才能确定。例如,ISO C11声明(我强调):如果argc的值大于零,则argv[0] * 指向的字符串表示 * 程序名;如果主机环境中没有程序名,则argv[0][0]应为空字符。所以不,它只是程序名,如果这个名字是 * 可用的。* 它 “代表” 程序名,不一定是 * 程序名。在此之前的部分指出:如果argc的值大于零,则数组成员argv[0]到argv[argc-1](含)应包含指向字符串的指针,这些字符串在程序启动之前由主机环境给出实现定义的值。这与之前的标准C99没有变化,这意味着即使是 * 值 * 也不是由标准决定的-完全取决于实现。这意味着如果宿主环境 * 不 * 提供程序名,则程序名可以为空,如果宿主环境 * 提供了程序名,则可以为任何其他名称,只要“其他任何内容”以某种方式表示程序名。在我更施虐的时刻,我会考虑将其翻译成斯瓦希里语,通过替换密码运行它,然后以反向字节顺序存储它:-)。然而,实现定义 * 在ISO标准中有特定的含义-实现必须记录它是如何工作的。因此,即使是UNIX,它可以通过exec系列调用将任何东西放入argv[0],也必须(并且确实)记录它。
argc
argv[0][0]
argv[argc-1]
exec
bxjv4tth2#
在具有exec*()调用的*nix类型系统中,argv[0]将是调用者在exec*()调用中放入argv0点的任何内容。shell使用的约定是这是程序名,大多数其他程序也遵循相同的约定,所以argv[0]通常是程序名。但是一个流氓Unix程序可以调用exec()并使argv[0]成为它喜欢的任何东西,所以不管C标准怎么说,你不能100%地指望这一点。
exec*()
*nix
argv0
exec()
e5njpo683#
根据C++标准,第3.6.1节:argv[0]应该是指向NTMBS的初始字符的指针,该字符表示用于调用程序的名称或“”所以,不,这是不保证的,至少标准。
ljo96ir54#
ISO-IEC 9899规定:
5.1.2.2.1程序启动
如果**argc的值大于零,则argv[0]指向的字符串表示程序名;argv[0][0]应为空字符,如果程序名称在主机环境中不可用。如果argc的值大于1,则由argv[1]到argv[argc-1]**指向的字符串表示 * 程序参数 *。我还使用了:
argv[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) */
然后,您只需解析字符串,从路径中提取可执行文件的名称。
xmq68pz95#
可执行文件名为argv[0] !=的应用程序
argv[0] !=
argv[0][0] == '-'
/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
getty
-
/bin/sh
/bin/ls
/bin/busybox
这使得有可能有一个小的静态链接的可执行文件,代表多个工具,并将工作在基本上任何Linux环境。标签:https://unix.stackexchange.com/questions/315812/why-does-argv-include-the-program-name/315817
可运行的POSIX execve示例,其中argv[0] !=可执行文件名
execve
其他人提到了exec,但这里是一个可运行的示例。a.c
#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上测试。
1yjd4xko6#
This page声明:元素argv[0]通常包含程序的名称,但这不应该被依赖-无论如何,一个程序不知道自己的名称是不寻常的!然而,其他页面似乎支持它总是可执行文件的名称的事实。This one声明:你会注意到argv[0]是程序本身的路径和名称。这允许程序发现关于自身的信息。它还向程序参数数组中添加了一个参数,因此在获取命令行参数时的一个常见错误是在需要argv[1]时抓取argv[0]。
xiozqbni7#
我不确定这是一个几乎普遍的惯例还是一个标准,但无论如何,你都应该遵守它。不过,我从未见过它在Unix和类Unix系统之外被利用。在Unix环境中,特别是在过去,程序可能会有明显不同的行为,这取决于它们被调用的名称。编辑:我从其他帖子中看到,有人已经确定它来自一个特定的标准,但我敢肯定,公约早在标准。
9nvpjoqh8#
如果你启动一个Amiga程序,将不会设置argv[0],只能通过CLI。
8条答案
按热度按时间yc0p9oo01#
猜测(即使是有根据的猜测)很有趣,但您确实需要查看标准文档才能确定。例如,ISO C11声明(我强调):
如果
argc
的值大于零,则argv[0]
* 指向的字符串表示 * 程序名;如果主机环境中没有程序名,则argv[0][0]
应为空字符。所以不,它只是程序名,如果这个名字是 * 可用的。* 它 “代表” 程序名,不一定是 * 程序名。在此之前的部分指出:
如果
argc
的值大于零,则数组成员argv[0]
到argv[argc-1]
(含)应包含指向字符串的指针,这些字符串在程序启动之前由主机环境给出实现定义的值。这与之前的标准C99没有变化,这意味着即使是 * 值 * 也不是由标准决定的-完全取决于实现。
这意味着如果宿主环境 * 不 * 提供程序名,则程序名可以为空,如果宿主环境 * 提供了程序名,则可以为任何其他名称,只要“其他任何内容”以某种方式表示程序名。在我更施虐的时刻,我会考虑将其翻译成斯瓦希里语,通过替换密码运行它,然后以反向字节顺序存储它:-)。
然而,实现定义 * 在ISO标准中有特定的含义-实现必须记录它是如何工作的。因此,即使是UNIX,它可以通过
exec
系列调用将任何东西放入argv[0]
,也必须(并且确实)记录它。bxjv4tth2#
在具有
exec*()
调用的*nix
类型系统中,argv[0]
将是调用者在exec*()
调用中放入argv0
点的任何内容。shell使用的约定是这是程序名,大多数其他程序也遵循相同的约定,所以
argv[0]
通常是程序名。但是一个流氓Unix程序可以调用
exec()
并使argv[0]
成为它喜欢的任何东西,所以不管C标准怎么说,你不能100%地指望这一点。e5njpo683#
根据C++标准,第3.6.1节:
argv[0]应该是指向NTMBS的初始字符的指针,该字符表示用于调用程序的名称或“”
所以,不,这是不保证的,至少标准。
ljo96ir54#
ISO-IEC 9899规定:
5.1.2.2.1程序启动
如果**
argc
的值大于零,则argv[0]
指向的字符串表示程序名;argv[0][0]
应为空字符,如果程序名称在主机环境中不可用。如果argc
的值大于1,则由argv[1]
到argv[argc-1]
**指向的字符串表示 * 程序参数 *。我还使用了:
然后,您只需解析字符串,从路径中提取可执行文件的名称。
xmq68pz95#
可执行文件名为
argv[0] !=
的应用程序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/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
,但这里是一个可运行的示例。a.c
b.c
然后又道:
提供:
是的,
argv[0]
也可以是:在Ubuntu 16.10上测试。
1yjd4xko6#
This page声明:
元素argv[0]通常包含程序的名称,但这不应该被依赖-无论如何,一个程序不知道自己的名称是不寻常的!
然而,其他页面似乎支持它总是可执行文件的名称的事实。This one声明:
你会注意到argv[0]是程序本身的路径和名称。这允许程序发现关于自身的信息。它还向程序参数数组中添加了一个参数,因此在获取命令行参数时的一个常见错误是在需要argv[1]时抓取argv[0]。
xiozqbni7#
我不确定这是一个几乎普遍的惯例还是一个标准,但无论如何,你都应该遵守它。不过,我从未见过它在Unix和类Unix系统之外被利用。在Unix环境中,特别是在过去,程序可能会有明显不同的行为,这取决于它们被调用的名称。
编辑:我从其他帖子中看到,有人已经确定它来自一个特定的标准,但我敢肯定,公约早在标准。
9nvpjoqh8#
如果你启动一个Amiga程序,将不会设置argv[0],只能通过CLI。