【问题标题】:c++ how does compiler get the length of argv stringsc++编译器如何获取argv字符串的长度
【发布时间】:2020-06-12 19:21:59
【问题描述】:

在 C/C++ 中,argc 是命令行参数的计数,argv 是 char** 或指向字符指针的指针。我知道 argc 可用于获取参数的数量,但是编译器如何知道第一个参数或第二个参数的长度?

【问题讨论】:

  • 它没有。这是从操作系统传递给你的程序的东西
  • nul 终止的字符串。

标签: c++ c


【解决方案1】:

编译器argv的内容一无所知。由您的编译器供应商提供并链接到您的可执行文件的 运行时库 将在创建进程时从操作系统获取命令行参数,然后该库将分配其自己的 @987654322 数组@ 指向 以空字符结尾的字符串 的指针,这些字符串从操作系统提供的数据中复制而来。然后将该数组通过argv 传递给您的main(),并将argc 设置为数组中有效char* 指针的数量。 main() 退出后,运行时将为该数组释放内存。

【讨论】:

    【解决方案2】:

    像所有 C 字符串一样,argv 指向的字符串是 0 终止的。

    编译器不知道。 (不能。当程序用一些实际参数调用时,编译器早就完成了。)但是程序可以使用strlen 找到。

    【讨论】:

      【解决方案3】:

      操作系统通常将所有参数和环境字符串放在堆栈段上,就在函数main()的第一个堆栈帧上方,然后argv数组也被堆栈,最后argc变量是传递给main()。因为它们位于main() 的堆栈框架之上,代码在用户空间中也调用您的信号处理程序(信号处理程序确实在用户模式下执行)。你隐藏了所有这些东西,因为你不需要知道它们在哪里。这发生在 linux 和几乎所有的 unix 衍生产品中。结构类似于:

      • 一种结构,允许外部程序知道参数列表和环境从哪里开始,以访问堆栈顶部。
      • 当系统返回到用户代码并且需要调用信号处理程序时调用信号处理程序的代码。由于上述结构的长度是固定的,因此只需知道堆栈段的开始位置即可访问这两个内容。
      • 所有环境字符串。这是可变长度。
      • 所有命令行参数。也是可变长度。
      • 两个指针数组,第一个带有指向环境字符串的指针(形式为“variable_name=value”),第二个指向命令行参数(两个数组都以和额外的 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"
      $ _
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-12-02
        • 2011-10-20
        • 2011-05-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多