一、main函数的参数解析
我们平常使用main函数是直接调用的,但是如果我们仔细观察的话,就会发现main函数其实也是有参数的,下面我们先看看main函数的原型:
int main (int argc, char *argv[ ], char *envp[ ])
{
program - statements
}
我们可以看见,main函数是有三个参数的:
第一个参数:argc是一个整型变量,表示命令行参数的个数(含第一个参数);
第二个参数:argv是个字符指针数组,每个元素是一个字符指针,指向一个字符串。这些字符串就是命令行中的每一个参数(字符串);
第三个参数:envp是字符指针的数组,数组的每一个元素是一个指向一个环境变量(字符串)的字符指针。
我们来看下面一段代码:
#include<stdio.h>
int main (int argc, char *argv[ ], char *envp[ ])
{
int i = 0;
for(i = 0; i < argc; i++)
{
printf("%s\n", argv[i] );
}
return 0;
}
输出的结果为:
当我们运行可执行程序test12_10_.exe的时候加入参数hello world,产生的结果如下图:
最后再结合代码我们可以这样解释:
**注:**argv数组的最后一个元素存放可一个NULL指针。
那第三个参数怎么解释呢?
#include<stdio.h>
int main (int argc, char *argv[ ], char *envp[ ] )
{
int i = 0;
while(envp [i] != NULL)
{
printf("%s\n",envp [i]);
i++;
}
return 0;
}
执行结果为:
结果显示的都是环境变量。
**注:**envp数组的最后一个元素也存放NULL指针。
二、可变参数列表解析
C语言中的可变参数是一个比较有意思的实现,通过将函数实现为可变参数的形式,可以使得函数可以接受1个以上的任意多个参数(不固定)。
先来看一个例子:
实现一个函数可以求任意个参数的平均值。
#include<stdio.h>
#include<stdarg.h>
int average(int n, ...)
{
va_list arg;
int i = 0;
int sum = 0;
va_start(arg, n);
for(i = 0;i <= n; i++)
{
sum += va_arg(arg, int);
}
return sum/n;
va_end(arg);
}
int main ()
{
int a = 1;
int b = 2;
int c = 3;
int avg1 = average(2, a, c );
int avg2 = average(3, a, b, c);
printf()"avg1 = %d\n", avg1);
printf()"avg2 = %d\n", avg2);
return 0;
}
声明一个va_list 类型的变量argv,它用于访问参数列表的未确定部分;
这个变量是调用va_start来初始化的,它的第一个参数是va_list的变量名,第二个参数是省略号前最后一个有名字的参数,初始化过程把arg变量设置为指向可变参数部分的第一个参数;
为了访问参数,需要使用va_arg ,这个宏接收两个参数:va_list变量和参数列表中下一个参数的类型。在这个例子中所有使用的可变参数都是整形。va_arg 返回这个参数的值,并使用va_arg`指向下一个可变参数。
最后,当访问完毕最后一个可变参数之后,我们需要调用va_end。
可变参数的限制
注意:可变参数必须从头到尾逐个访问。如果你在访问了几个可变参数之后想半途终止,这是可以的,但是如果你想一开始就访问参数列表中间的参数,那是不行的。
参数列表中至少有一个命名参数。如果连一个命名参数都没有,就无法使用va_start。
这些宏是无法直接判断实际存在参数的数量。
这些宏无法判断每个参数的类型。
如果在va_arg中制定了错误的类型,那么其后果是不可预测的。
我们可以研究一下可变参数实现的细节:
在VS的源码中我们可以看到
可变参数的实现过程是使用宏的封装,只要完成替换,皆可以自行分析了。