【发布时间】:2020-06-12 19:21:59
【问题描述】:
在 C/C++ 中,argc 是命令行参数的计数,argv 是 char** 或指向字符指针的指针。我知道 argc 可用于获取参数的数量,但是编译器如何知道第一个参数或第二个参数的长度?
【问题讨论】:
-
它没有。这是从操作系统传递给你的程序的东西
-
nul 终止的字符串。
在 C/C++ 中,argc 是命令行参数的计数,argv 是 char** 或指向字符指针的指针。我知道 argc 可用于获取参数的数量,但是编译器如何知道第一个参数或第二个参数的长度?
【问题讨论】:
编译器对argv的内容一无所知。由您的编译器供应商提供并链接到您的可执行文件的 运行时库 将在创建进程时从操作系统获取命令行参数,然后该库将分配其自己的 @987654322 数组@ 指向 以空字符结尾的字符串 的指针,这些字符串从操作系统提供的数据中复制而来。然后将该数组通过argv 传递给您的main(),并将argc 设置为数组中有效char* 指针的数量。 main() 退出后,运行时将为该数组释放内存。
【讨论】:
像所有 C 字符串一样,argv 指向的字符串是 0 终止的。
编译器不知道。 (不能。当程序用一些实际参数调用时,编译器早就完成了。)但是程序可以使用strlen 找到。
【讨论】:
操作系统通常将所有参数和环境字符串放在堆栈段上,就在函数main()的第一个堆栈帧上方,然后argv数组也被堆栈,最后argc变量是传递给main()。因为它们位于main() 的堆栈框架之上,代码在用户空间中也调用您的信号处理程序(信号处理程序确实在用户模式下执行)。你隐藏了所有这些东西,因为你不需要知道它们在哪里。这发生在 linux 和几乎所有的 unix 衍生产品中。结构类似于:
NULL 指示结束的指针结束数组)。envp,遗留,C 标准并没有声明环境可以作为main() 的第三个参数访问,但一些遗留代码假定,所以我认为它仍在完成所以)argc)int,命令行参数数量为argv。main() 的返回值。main() 的参数通常作为返回指针上方的第一个和第二个参数访问。
这是main()的调用方式,这部分由操作系统填充(主要是字符串、数组、信号处理程序代码和第一个结构,用于定位参数和环境的指针),在将控制权交给运行时之前,部分由 C 标准运行时填充(argc 参数计数、argv 指针和envp 指针)。然后调用main(),将调用的返回地址推送到main()。
只是说这只是一种可能的实现,并且没有强制运行时这样做,但它是从可追溯到 pdp-11 实现的遗留代码中假设的,因此在许多平台上仍然以这种方式实现.
检查这个程序(它会打印它的命令行参数和环境):
#include <stdio.h>
int main(int argc, char **argv, char **envp)
{
printf(
"%14p: argc(=%d)\n"
"%14p: argv\n"
"%14p: envp\n",
&argc, argc,
&argv,
&envp);
printf(
"%14p: argv[]\n"
"%14p: envp[]\n",
argv,
envp);
int i;
for (i = 0; i <= argc; i++) {
printf("%2$14p: argv[%1$d] => \"%2$s\"\n",
i, argv[i]);
}
for (i = 0; envp[i]; i++) {
printf("%2$14p: envp[%1$d] => \"%2$s\"\n",
i, envp[i]);
}
/* .. and NULL environment final pointer */
printf("%2$14p: envp[%1$d] => \"%2$s\"\n",
i, envp[i]);
return 0;
}
并用不同的参数列表调用它,看看它如何打印参数列表,然后是环境字符串。
这是我系统上调用的输出:
$ a.out a b c | sort | less
0x0: argv[4] => "(null)"
0x0: envp[36] => "(null)"
0x7fffffffe368: envp
0x7fffffffe370: argv
0x7fffffffe378: argc(=4)
0x7fffffffe3f0: argv[]
0x7fffffffe418: envp[]
0x7fffffffe6f0: argv[0] => "pru72524"
0x7fffffffe6f9: argv[1] => "a"
0x7fffffffe6fb: argv[2] => "b"
0x7fffffffe6fd: argv[3] => "c"
0x7fffffffe6ff: envp[0] => "SHELL=/usr/local/bin/bash"
0x7fffffffe719: envp[1] => "WINDOWID=18874381"
0x7fffffffe72b: envp[2] => "XTERM_VERSION=XTerm(354)"
0x7fffffffe744: envp[3] => "TERMCAP=SC|screen|VT 100/ANSI X3.64 virtual termin>
0x7fffffffeaed: envp[4] => "JAVA_HOME=/usr/local/openjdk8"
0x7fffffffeb0b: envp[5] => "SSH_AUTH_SOCK=/tmp/ssh-0ssuWLqLKLsr/agent.1659"
0x7fffffffeb3a: envp[6] => "WINDOW=6"
0x7fffffffeb43: envp[7] => "ANT_HOME=/usr/local/share/java/apache-ant"
0x7fffffffeb6d: envp[8] => "SSH_AGENT_PID=1660"
0x7fffffffeb80: envp[9] => "XTERM_SHELL=/usr/local/bin/bash"
0x7fffffffeba0: envp[10] => "EDITOR=vi"
0x7fffffffebaa: envp[11] => "PWD=/home/lcu"
0x7fffffffebb8: envp[12] => "MAKEOBJDIRPREFIX=/home/lcu/obj"
0x7fffffffebd7: envp[13] => "LOGNAME=lcu"
0x7fffffffebe3: envp[14] => "TZ=Europe/Helsinki"
0x7fffffffebf6: envp[15] => "WINDOWPATH=9"
0x7fffffffec03: envp[16] => "LDFLAGS=-g"
0x7fffffffec0e: envp[17] => "HOME=/home/lcu"
0x7fffffffec1d: envp[18] => "LANG=es_ES.UTF-8"
0x7fffffffec2e: envp[19] => "XTERM_LOCALE=es_ES.UTF-8"
0x7fffffffec47: envp[20] => "TERM=screen"
0x7fffffffec53: envp[21] => "USER=lcu"
0x7fffffffec5c: envp[22] => "NETBEANS_HOME=/home/opt/netbeans"
0x7fffffffec7d: envp[23] => "MAVEN_HOME=/usr/local/share/java/maven"
0x7fffffffeca4: envp[24] => "DISPLAY=:0"
0x7fffffffecaf: envp[25] => "FORTUNE_PATH=/home/lcu/.fortune:/usr/share/games/>
0x7fffffffece8: envp[26] => "SHLVL=1"
0x7fffffffecf0: envp[27] => "PAGER=less -EFGRS"
0x7fffffffed02: envp[28] => "LD_LIBRARY_PATH=/home/lcu/lib:/home/lcu/lib:"
0x7fffffffed2f: envp[29] => "MAVEN_OPTS=-Xmx2048m"
0x7fffffffed44: envp[30] => "PATH=/home/lcu/bin:.:/usr/local/share/java/apache>
0x7fffffffef10: envp[31] => "STY=2430.pts-1.europa"
0x7fffffffef26: envp[32] => "BLOCKSIZE=K"
0x7fffffffef32: envp[33] => "CFLAGS=-g -O0 -DHAS_LIBUTIL_H"
0x7fffffffef50: envp[34] => "MAIL=/var/mail/lcu"
0x7fffffffef63: envp[35] => "_=./pru72524"
$ _
【讨论】: