main函数具有三个参数这三个参数分别为:
int main(int argc, char*argv[], char*envp[])
{
return 0;
}
我们先来看看前两个参数的意义,第一个参数是一个整数,这个整数就是记录当前程序所具有的子串个数,第二个参数是一个字符指针数组,数组里面的每一个指针指向的就是当前程序的每个子串,大家看到这里肯定还是不大懂这两个参数是什么意思,那么我们来看看下面这段代码:
对吧!你说argv里面装的是子串,argc表示的是子串的个数,那这里我就用for循环来查看一下这里的字串的内容到底是什么?上面的代码运行结果如下:
我们发现这段代码没有运行成功,但是这里错误的原因并不是我们的代码写错了而是这里的makefile的编译方法写错了,当前的编译是采用c99标准执行的但是使用main函数的参数得用c11标准所以这里就报错了,那么这里得对编译方法进行修改改成下面的样子就能够编译成功:
gcc myproc.c -o myproc -g -std=c11
执行一下make然后再执行可执行程序就可以看到这里的运行结果如下:
运行程序时输入的指令为./myproc,而argv数组中第一个元素的内容也是./myproc并且该数组中就只有一个有效内容那这是不是有点巧合啊,我们再试一下其他的执行情况在./myproc后面添加一些选项比如说:./myproc -a -b -c -d
再运行一下这个程序看看运行结果如何:
当指令中添加选项时argv数组的元素个数也会随之变多并且数组的内容也就是运行这个程序所使用的指令,输入的指令为:./myproc -a -b -c -d
实际上它就是一个大字符串,在执行这个指令的时候会将这个字符串以空格为分隔符分成若干个子串也就是说./myproc -a -b -c -d
会被分成./myproc
,-a
,-b
,-c
,-d
,然后这些子串就会以从左往右的顺序依次放入argv数组,而整型变量argc就是记录指令所产生的子串的个数,大家可以通过下面的图来了解了解这里的运行过程:
这里大家还要注意的一点就是argv数组的最后一个元素是空指针,我们可以通过下面的代码进行验证:
如果最后一个元素不是空指针的话这个循环会打印一些随机值,将这段代码运行一下就可以看到这里没有打印出来随机值,那么这就说明argv数组的最后一个元素为空指针:
我们平时在使用指令的时候会添加很多的选项比如说ls指令,单独使用ls指令会将当前路径下的每个可见文件的文件名全部显示出来并且一行显示多个文件名:
如果给ls指令添加-a选项就可以显示隐藏文件的文件名并且也是一行显示多个:
如果给ls指令添加-l选项那么就可以显示当前路径下的所有可见文件的详细信息,并且一行显示一个:
ls -l指令还可以简化成为ll并且功能还是一样的
有些选项还可以结合在一起使用比如说-a选项是显示所有文件,-l选项是显示可见文件的详细信息,那-al选项就是显示所有文件的详细信息
那么上述功能的原理就和整型变量argc数组argv有关,argv数组中存储着输入的指令和选项,那么根据数组中的内容和else if语句就可以实现不同的选项执行不同的功能,比如说下面的代码:
#include
2 #include<stdlib.h>
3 #include<string.h>
4 int main(int argc,char*argv[])
5 {
6 if(argc>=3)
7 {
8 printf("指令使用错误最多支持一个选项\n");
return 0;
9 }
10 if(argc==1)
11 {
12 printf("开始执行功能一\n");
13 }
14 else
15 {
16 if(strcmp(argv[1],"-a")==0)
17 {
18 printf("开始执行功能二\n");
19 //对应的功能
20 }
21 else if(strcmp(argv[1],"-b")==0)
22 {
23 printf("开始执行功能三\n");
24 //对应的功能
25 }
26 else if(strcmp(argv[1],"-c")==0)
27 {
28 printf("开始执行功能四\n");
29 //对应的功能
30 }
31 else if(strcmp(argv[1],"-d")==0)
32 {
33 printf("开始执行功能五\n");
34 //对应的功能
35 }
36 else if(strcmp(argv[1],"-ab")==0)
37 {
38 printf("开始执行功能六\n");
39 //对应的功能
40 }
41 else if(strcmp(argv[1],"-ac")==0)
42 {
43 printf("开始执行功能七\n");
44 //对应的功能
45 }
46 else if(strcmp(argv[1],"-ad")==0)
47 {
48 printf("开始执行功能八\n");
49 //对应的功能
50 }
51 else
52 {
53 printf("选项输入有误\n");
return 0;
54 }
55 }
56 return 0;
57 }
我们可以测试一下上面的代码,当一个选项都不带的时候就应该执行功能一:
添加-a选项的话就应该执行功能二:
添加-ab选项的话就应该执行功能六:
如果添加的选项过多或者选项不存在的话就会告诉使用者选项有误:
那么这就是main函数中的前两个参数的使用场景,有了这两个参数我们写的程序就可以根据不同的执行指令来执行不同的功能希望大家能够理解。
int main(int argc, char*argv[], char*envp[])
{
return 0;
}
main函数的第三个参数也是一个字符指针数组,与第二个参数不同的是这个数组里面的指针指向的是环境变量里面的内容,相同的是这个数组也是以空指针作为该数组的最后一个元素结尾,比如说下面的图片:
也就是说我们可以通过第三个参数来得到环境变量的内容比如说下面的代码:
#include
2 #include<stdlib.h>
3 #include<string.h>
4 int main(int argc,char*argv[],char*envp[])
5 {
6 int i=0;
7 while(envp[i])
8 {
9 printf("%s\n",envp[i]);
10 ++i;
11 }
12 return 0;
13 }
这段代码的运行结果如下;
我们可以看到通过数组envp也可以在程序内部打印出来环境变量的内容,那这里大家有没有想过一个问题,main函数为什么要加第三个参数呢?我们可以getenv函数来获取环境变量的内容啊。为什么还要多此一举呢?原因很简单getenv函数是给我们程序员使用的如果想要获取某一个环境变量的内容的话应该使用getenv函数,而envp数组是给子进程使用的子进程是一个笼统的概念,我们可能会在子进程中使用各种各样的功能所以环境变量的每个值可能都要得到,所以就会有这个数组方便子进程使用环境变量,我们之前说环境变量具有全局属性在任意的进程中都能使用,它之所以有全局属性的原因是在创建子进程的时候会继承环境变量,那子进程是如何继承环境变量的呢?答案就是通过main函数的第三个参数来实现继承环境变量希望大家能够理解。我们把envp数组看成一个表,表中的每个元素看成一个指针,指针指向的内容就是一个个的环境变量,那么这里linux还提供给我们一个二级指针,这个指针就指向的是表中的首元素:
所以这里我们就可以通过environ这个二级指针来获取环境变量,比如说下面的代码:
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<string.h>
4 int main(int argc,char*argv[],char*envp[])
5 {
6 extern char**environ;
7 int i=0;
8 while(environ[i])
9 {
10 printf("environ[%d] : %s\n",i,environ[i]);
11 ++i;
12 }
13 return 0;
14 }
这段代码的运行结果如下:
子进程在继承父进程的环境变量的时候是通过main函数的第三个参数实现的,而第三个参数是一个字符指针数组,数组在传参的时候传的是首元素的地址,所以子进程在继承环境变量的时候,系统得传一个二级指针那么这个指针就是上面使用的char**environ,希望大家能够理解。