“argv[0] = name-of-executable”是一个公认的标准还是一个通用的约定?

mftmpeh8  于 2023-10-16  发布在  其他
关注(0)|答案(8)|浏览(130)

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

yc0p9oo0

yc0p9oo01#

猜测(即使是有根据的猜测)很有趣,但您确实需要查看标准文档才能确定。例如,ISO C11声明(我强调):
如果argc的值大于零,则argv[0] * 指向的字符串表示 * 程序名;如果主机环境中没有程序名,则argv[0][0]应为空字符。
所以不,它只是程序名,如果这个名字是 * 可用的。* 它 “代表” 程序名,不一定是 * 程序名。在此之前的部分指出:
如果argc的值大于零,则数组成员argv[0]argv[argc-1](含)应包含指向字符串的指针,这些字符串在程序启动之前由主机环境给出实现定义的值。
这与之前的标准C99没有变化,这意味着即使是 * 值 * 也不是由标准决定的-完全取决于实现。
这意味着如果宿主环境 * 不 * 提供程序名,则程序名可以为空,如果宿主环境 * 提供了程序名,则可以为任何其他名称,只要“其他任何内容”以某种方式表示程序名。在我更施虐的时刻,我会考虑将其翻译成斯瓦希里语,通过替换密码运行它,然后以反向字节顺序存储它:-)。
然而,实现定义 * 在ISO标准中有特定的含义-实现必须记录它是如何工作的。因此,即使是UNIX,它可以通过exec系列调用将任何东西放入argv[0],也必须(并且确实)记录它。

bxjv4tth

bxjv4tth2#

在具有exec*()调用的*nix类型系统中,argv[0]将是调用者在exec*()调用中放入argv0点的任何内容。
shell使用的约定是这是程序名,大多数其他程序也遵循相同的约定,所以argv[0]通常是程序名。
但是一个流氓Unix程序可以调用exec()并使argv[0]成为它喜欢的任何东西,所以不管C标准怎么说,你不能100%地指望这一点。

e5njpo68

e5njpo683#

根据C++标准,第3.6.1节:
argv[0]应该是指向NTMBS的初始字符的指针,该字符表示用于调用程序的名称或“”
所以,不,这是不保证的,至少标准。

ljo96ir5

ljo96ir54#

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) */

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

xmq68pz9

xmq68pz95#

可执行文件名为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,但这里是一个可运行的示例。
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上测试。

1yjd4xko

1yjd4xko6#

This page声明:
元素argv[0]通常包含程序的名称,但这不应该被依赖-无论如何,一个程序不知道自己的名称是不寻常的!
然而,其他页面似乎支持它总是可执行文件的名称的事实。This one声明:
你会注意到argv[0]是程序本身的路径和名称。这允许程序发现关于自身的信息。它还向程序参数数组中添加了一个参数,因此在获取命令行参数时的一个常见错误是在需要argv[1]时抓取argv[0]。

xiozqbni

xiozqbni7#

我不确定这是一个几乎普遍的惯例还是一个标准,但无论如何,你都应该遵守它。不过,我从未见过它在Unix和类Unix系统之外被利用。在Unix环境中,特别是在过去,程序可能会有明显不同的行为,这取决于它们被调用的名称。
编辑:我从其他帖子中看到,有人已经确定它来自一个特定的标准,但我敢肯定,公约早在标准。

9nvpjoqh

9nvpjoqh8#

如果你启动一个Amiga程序,将不会设置argv[0],只能通过CLI。

相关问题