我正在使用此代码。。。
#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
为什么没有错误?为什么要生成这些与我的操作系统相关的变量?
越界访问数组会导致未定义的行为。未定义行为的后果是未定义。纯属巧合,你碰巧正在读取一些环境变量。在其他系统上,或在此类行为的其他示例中,您可能会意外地读取其他信息(请参阅heartsleed了解一个显著的示例),或意外地导致segfault(崩溃)。。。然而,所有这些结果都纯属巧合,不可依赖。
Unix系统上程序的正常内存布局是,指向程序参数的指针数组后面紧跟着指向环境变量的指针数组。
您正在做的是超越参数指针数组(注意argv[argc]
处有一个空指针),进入环境指针数组,该数组也由一个空指示器终止。
未定义的行为
因为您正在超越argv
数组的末尾,所以您正在调用未定义的行为。这很危险:任何事情都有可能发生。不过,有时结果是好的——但你永远不能依赖它。
有了强制性免责声明,您可以使用以下代码获得环境的完整转储。但是,必须理解C标准不能保证清楚标记线之后的行为(POSIX也不能)。
main()
的额外envp
自变量在C标准的附录J.5.1中作为一个通用扩展进行了说明,该扩展由Microsoft Windows和Unix系统支持。
J.5.1环境论点
在托管环境中,
main
函数接收第三自变量char *envp[]
,指向指向char
的以null结尾的指针数组,每个指针指向一个字符串为程序的执行提供有关环境的信息(5.1.2.2.1).
这不是标准规定的;它被标准认可为通用的。在POSIX系统上,还可以通过全局变量char **environ
获取环境变量。而且,如果使用setenv()
或putenv()
操作环境,则无法保证存储在environ
中的值与传递给main()
的envp
中存储的值保持相同。
#include <assert.h>
#include <stdio.h>
int main(int argc, char **argv, char **envp)
{
int i;
printf("Argc: %dn", 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: %dn", (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: %dn", 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
$
不会"生成"任何额外的参数。相反,由于C没有边界检查(与其他一些语言不同),您访问了数组边界之外的内存,而数组边界恰好是环境变量。