可以生成多少个参数以及为什么会生成它们?

How many arguments can be generated and why are they generated?

我正在使用此代码...

#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<4;i++) 更改为 for (i=0;i<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 

为什么没有报错?为什么会生成这些与我的OS相关的变量?

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

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

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

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

未定义的行为

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

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

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

J.5.1 Environment arguments

In a hosted environment, the main function receives a third argument, char *envp[], that points to a null-terminated array of pointers to char, each of which points to a string that provides information about the environment for this execution of the program (5.1.2.2.1).

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

#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
$