args[0]是否保证是执行路径的路径?

11
这是一个基本的问题,但同样重要...
当启动一个C++程序时,其主方法具有以下常见的签名:
int main(int argc, char* args[]) {
    //Magic!
    return 0;
}

args[0]是否始终保证是当前运行程序的路径?跨平台情况下又如何(因为我目前在Linux环境中,但以后可能会移植)?
4个回答

25

这并非总是如此。它取决于操作系统向程序提供的值。例如,当使用exec启动程序时,您可以将其设置为任意值:

int execve(const char *filename, char *const argv[],
           char *const envp[]);

第一个参数是要启动的文件,argv将包含argv[0]和main的所有其他参数。envp包含环境变量(不是由标准C或C++定义的,这是一种posix的东西)。

更确切地说,这是C++中argv的定义:

实现不应预定义main函数。此函数不得重载。它应具有类型int的返回类型,但其类型否则为实现定义。所有实现都应允许main的以下两个定义:

int main() { /* ... */ }

int main(int argc, char* argv[]) { /* ... */ }

在后一种形式中,argc应该是从程序运行的环境中传递给程序的参数数量。如果argc为非零值,则这些参数应作为指向以空字符结尾的多字节字符串(NTMBS)(17.3.2.1.3.2)的初始字符的指针通过argv[0]到argv[argc-1]提供,并且argv[0]应该是表示调用程序名称的NTMBS的初始字符的指针或""。argc的值应为非负数。argv[argc]的值应为0。[注意:建议在argv之后添加任何进一步(可选)参数。]
“用于调用程序的名称”由实现定义。如果您想获取可执行文件的完整路径,则可以在Windows上使用GetModuleFileName,并将argv[0](用于获取执行名称,可能是相对的)与getcwd(用于获取当前工作目录,试图使名称绝对)一起使用。

例如,一个 shell 知道它是登录 shell 是因为 login(1) 安排让 argv[0] 以破折号开头。 - Norman Ramsey
1
... 而 bash 可以通过比较 argv [0] 与 "sh",知道它处于 POSIX 模式 :) 顺便说一句,这个 argv 技巧是使 busybox 如此小的原因。所有实用程序都打包在一个二进制文件中,只需使用不同的 argv [0] 值执行即可。 - Johannes Schaub - litb
我会在可移植程序中避免使用realpath。POSIX realpath接口存在问题,因为您无法指定目标缓冲区的大小,也不能分配足够大的缓冲区而不对PATH_MAX做出假设。 - Doug

8

在Windows上,GetModuleFileName可以确保获得当前正在执行的程序的完整路径。在Linux上,有一个名为/proc/self/exe的符号链接。对此符号链接执行readlink操作以获取当前正在执行的程序的完整路径。即使您的程序是通过/proc/self/exe符号链接调用的,它也始终指向实际程序。


4

以前学生在学校的主机上玩Rogue游戏时,为了隐藏这一事实,他们会编写C程序,并将argv [0]设置为“cc”或“tcsh”来启动它,但这并不是保证。


2

以下是C标准对argv[0]的规定:

如果argc的值大于零,则由argv[0]指向的字符串表示程序名称;如果主机环境中没有可用的程序名称,则argv[0][0]应为null字符。

至于它是否包含完整路径,答案是argv[0]不一定包含可执行文件的完整路径。在Windows上,它似乎正是在命令行中提供的内容。不知道Linux / Unix会怎么做。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接