【问题标题】:How many arguments can be generated and why are they generated?可以生成多少个参数,为什么会生成它们?
【发布时间】:2015-04-04 13:14:30
【问题描述】:

我正在使用此代码...

#include <stdio.h>

int main(int argc,char *argv[]){
    int i =0;
    if (argc == 1){
        printf("You have entered 1 argument and you suck \n");
    }
    else if (argc > 1 && argc<4){
        for (i=0;i<4;i++){
            printf("you have entered the following args %d.%s \n",(i),argv[i]);
        }
        printf("\n");
    }
    else{
        printf("You have entered more than four arguments and you suck.\n");
    }
    return 0;
}

如果我将 for 循环从 for (i=0;i&lt;4;i++) 更改为 for (i=0;i&lt;7;i++),我会得到以下输出:

cam@cam:~/Desktop/D/c/c-code$ ./ex12 hi there 
you have entered the following args 0../ex12 
you have entered the following args 1.hi 
you have entered the following args 2.there 
you have entered the following args 3.(null) 
you have entered the following args 4.XDG_VTNR=7 
you have entered the following args 5.LC_PAPER=en_IN.UTF-8 
you have entered the following args 6.ORBIT_SOCKETDIR=/tmp/orbit-cam 

为什么没有错误?为什么会生成这些与我的操作系统相关的变量?

【问题讨论】:

  • 我的问题是为什么环境变量?我知道索引超出范围。我故意更改它以查看会发生什么
  • 任何事情都可能发生。代码可以打印你的环境变量,可以格式化硬盘,可以让独角兽在你的显示器前跳舞等等。未定义的行为未定义。

标签: c main


【解决方案1】:

越界访问数组会导致未定义的行为。未定义行为的后果是未定义。纯属巧合,您碰巧正在阅读一些环境变量。在其他系统上,或在此类行为的其他示例中,您最终可能会巧合地阅读一些其他信息(请参阅 heartbleed 以了解一个值得注意的示例)或巧合地导致段错误(崩溃)......然而,所有这些结果纯属巧合,不可依赖。

【讨论】:

    【解决方案2】:

    Unix 系统上程序的正常内存布局是指向程序参数的指针数组紧跟在指向环境变量的指针数组之后。

    您正在做的是超越参数指针数组(注意argv[argc] 处有一个空指针)并进入环境指针数组,该数组也由一个空指针终止。

    未定义的行为

    因为您超出了argv 数组的末尾,所以您正在调用未定义的行为。这是危险的:任何事情都可能发生。不过,有时结果是良性的——但是你永远不能依赖它。

    排除强制性免责声明, 您可以使用如下代码获得环境的完整转储。但是,您必须了解 C 标准不保证明确标记的行之后的行为(POSIX 也不保证)。

    main() 的额外 envp 参数在 C 标准的附件 J.5.1 中作为一个通用扩展被注明 - Microsoft Windows 和 Unix 系统都支持该扩展。

    J.5.1 环境参数

    在托管环境中,main 函数接收第三个参数 char *envp[], 它指向一个以空结尾的指向char 的指针数组,每个指针都指向一个字符串 为程序的执行提供有关环境的信息 (5.1.2.2.1)。

    标准没有强制要求;它被标准认可为普遍可用的。在 POSIX 系统上,您还可以通过全局变量 char **environ 获取环境变量。而且,如果您使用setenv()putenv() 操作环境,则无法保证environ 中存储的值与传递给envp 中存储的值相同main()

    #include <assert.h>
    #include <stdio.h>
    
    int main(int argc, char **argv, char **envp)
    {
        int i;
        printf("Argc: %d\n", argc);
        for (i = 0; i < argc; i++)
            printf("Arg[%d]: <<%s>>\n", i, argv[i]);
        assert(argv[argc] == 0);
        assert(argv[i] == 0);
    
       /* Print the environment cleanly on most systems */
        char **ep;
        for (ep = envp; *ep != 0; ep++)
            printf("Env[%d]: <<%s>>\n", (int)(ep - envp), *ep);
        printf("Number of environment variables: %d\n", (int)(ep -envp));
    
        /* From here on, neither the C standard nor POSIX guarantees anything */
        /* In practice, it works on Unix systems; I don't know about Windows */
        assert(&argv[argc+1] == envp);
        for (i++; argv[i] != 0; i++)
            printf("Env[%d]: <<%s>>\n", i - argc - 1, argv[i]);
        printf("Number of environment variables: %d\n", i - argc - 1);
        return 0;
    }
    

    请注意,即使 Windows 支持 main() 的第三个参数,也不能保证它指向的内存紧跟在 argv 指向的内存之后(尽管发现它是与 Unix 上相同的布局)。从技术上讲,Unix 上不能保证envp 数组紧跟在argv 数组之后;但是,我从来没有遇到过这样的系统。

    env 程序允许您为其运行的命令设置环境,-i 选项忽略任何继承的环境。这让我可以像这样运行上面的代码(编译成程序ev2):

    $ env -i HOME=/home/elephant PATH=/home/elephant/bin:/bin:/usr/bin TZ=US/Pacific \
    >     ./ev2 hello world
    Argc: 3
    Arg[0]: <<./ev2>>
    Arg[1]: <<hello>>
    Arg[2]: <<world>>
    Env[0]: <<HOME=/home/elephant>>
    Env[1]: <<PATH=/home/elephant/bin:/bin:/usr/bin>>
    Env[2]: <<TZ=US/Pacific>>
    Number of environment variables: 3
    Env[0]: <<HOME=/home/elephant>>
    Env[1]: <<PATH=/home/elephant/bin:/bin:/usr/bin>>
    Env[2]: <<TZ=US/Pacific>>
    Number of environment variables: 3
    $
    

    【讨论】:

      【解决方案3】:

      没有“生成”额外的参数。相反,由于 C 没有边界检查(与其他一些语言不同),因此您访问了数组边界之外的内存,而这恰好是环境变量。

      【讨论】:

      • @Ajay ,代码调用未定义行为,任何事情都可能发生。
      猜你喜欢
      • 1970-01-01
      • 2011-09-30
      • 1970-01-01
      • 1970-01-01
      • 2020-02-05
      • 1970-01-01
      • 1970-01-01
      • 2022-12-06
      相关资源
      最近更新 更多