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

发布于 2024-08-18 03:38:37 字数 113 浏览 18 评论 0原文

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

When passing argument to main() in a C or C++ application, will argv[0] always be the name of the executable? Or is this just a common convention and not guaranteed to be true 100% of the time?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

停顿的约定 2024-08-25 03:38:38

此页面指出:

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

然而,其他页面似乎支持这样一个事实:它始终是可执行文件的名称。 此内容指出:

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

This page states:

The element argv[0] normally contains the name of the program, but this shouldn't be relied upon - anyway it is unusual for a program not to know its own name!

However, other pages seem to back up the fact that it is always the name of the executable. This one states:

You’ll notice that argv[0] is the path and name of the program itself. This allows the program to discover information about itself. It also adds one more to the array of program arguments, so a common error when fetching command-line arguments is to grab argv[0] when you want argv[1].

停滞 2024-08-25 03:38:38

我不确定这是否是一个几乎通用的约定或标准,但无论哪种方式你都应该遵守它。不过,我从未见过它在 Unix 和类 Unix 系统之外被利用。在 Unix 环境中(尤其是在过去),程序可能会根据调用它们的名称而具有显着不同的行为。

编辑:我从与我同时发表的其他帖子中看到,有人已将其识别为来自特定标准,但我确信该约定早于该标准。

I'm not sure whether it is a nearly universal convention or a standard, but either way you should abide by it. I've never seen it exploited outside of Unix and Unix-like systems, though. In Unix environments - and maybe particularly in the old days - programs might have significantly different behaviors depending on the name under which they are invoked.

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.

望笑 2024-08-25 03:38:38

如果您通过 Workbench 启动 Amiga 程序,则不会设置 argv[0],只能通过 CLI 进行设置。

If you start an Amiga program by Workbench argv[0] will not be set, only by CLI.

携余温的黄昏 2024-08-25 03:38:37

猜测(甚至是有根据的猜测)很有趣,但您确实需要查看标准文档才能确定。例如,ISO C11 规定(我的重点):

如果argc的值大于零,则argv[0]指向的字符串代表 程序名称;如果程序名称在主机环境中不可用,则 argv[0][0] 应为空字符。

所以不,如果该名称可用,它只是程序名称。并且它“代表”程序名称,不一定程序名称。前面的部分指出:

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

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

这意味着,如果主机环境提供程序名称,则程序名称可以为空;如果主机环境提供提供程序名称,则程序名称可以为空,前提是“任何其他名称”以某种方式代表程序名称。在我更施虐的时刻,我会考虑将其翻译成斯瓦希里语,通过替换密码运行它,然后以相反的字节顺序存储它:-)。

然而,实现定义确实在 ISO 标准中具有特定含义 - 实现必须记录其工作方式。因此,即使 UNIX 可以通过 exec 系列调用将任何它喜欢的东西放入 argv[0] 中,也必须(并且确实)记录它。

Guesswork (even educated guesswork) is fun but you really need to go to the standards documents to be sure. For example, ISO C11 states (my emphasis):

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

So no, it's only the program name if that name is available. And it "represents" the program name, not necessarily is the program name. The section before that states:

If the value of argc is greater than zero, the array members argv[0] through argv[argc-1] inclusive shall contain pointers to strings, which are given implementation-defined values by the host environment prior to program startup.

This is unchanged from C99, the previous standard, and means that even the values are not dictated by the standard - it's up to the implementation entirely.

This means that the program name can be empty if the host environment doesn't provide it, and anything else if the host environment does provide it, provided that "anything else" somehow represents the program name. In my more sadistic moments, I would consider translating it into Swahili, running it through a substitution cipher then storing it in reverse byte order :-).

However, implementation-defined does have a specific meaning in the ISO standards - the implementation must document how it works. So even UNIX, which can put anything it likes into argv[0] with the exec family of calls, has to (and does) document it.

辞别 2024-08-25 03:38:37

在具有 exec*() 调用的 *nix 类型系统下,argv[0] 将是调用者放入 argv0< 中的任何内容/code> 位于 exec*() 调用中。

shell 使用的约定是程序名称,并且大多数其他程序也遵循相同的约定,因此 argv[0] 通常是程序名称。

但是流氓 Unix 程序可以调用 exec() 并生成 argv[0] 任何它喜欢的内容,所以无论 C 标准怎么说,你都不能指望这一点100% 的时间。

Under *nix type systems with exec*() calls, argv[0] will be whatever the caller puts into the argv0 spot in the exec*() call.

The shell uses the convention that this is the program name, and most other programs follow the same convention, so argv[0] usually the program name.

But a rogue Unix program can call exec() and make argv[0] anything it likes, so no matter what the C standard says, you can't count on this 100% of the time.

请你别敷衍 2024-08-25 03:38:37

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

argv[0] 是指向
NTMBS 的初始字符
代表用于调用的名称
程序或“”

所以不,至少标准不能保证。

According to the C++ Standard, section 3.6.1:

argv[0] shall be the pointer to the
initial character of a NTMBS that
represents the name used to invoke the
program or ""

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

水波映月 2024-08-25 03:38:37

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

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

ISO-IEC 9899 states:

5.1.2.2.1 Program startup

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[1] through argv[argc-1] represent the program parameters.

I've also used:

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

And then you just have to parse the string to extract the executable name from the path.

心意如水 2024-08-25 03:38:37

具有 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,但这里是一个可运行的示例。

ac

#define _XOPEN_SOURCE 700
#include <unistd.h>

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

bc

#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 上测试。

Applications of having argv[0] != executable name

  • many shells determine if they are a login shell by checking argv[0][0] == '-'. Login shells have different properties, notably that they source some default files such as /etc/profile.

    It is typically the init itself or getty that adds the leading -, see also: https://unix.stackexchange.com/questions/299408/how-to-login-automatically-without-typing-the-root-username-or-password-in-build/300152#300152

  • multi-call binaries, perhaps most notably Busybox. These symlink multiple names e.g. /bin/sh and /bin/ls to a single exebutable /bin/busybox, which recognizes which tool to use from argv[0].

    This makes it possible to have a single small statically linked executable that represents multiple tools, and will work on basically on any Linux environment.

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

Runnable POSIX execve example where argv[0] != executable name

Others mentioned exec, but here is a runnable example.

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]);
}

Then:

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

Gives:

yada yada

Yes, argv[0] could also be:

Tested on Ubuntu 16.10.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文