可变参数列表讲解《一》(简单介绍使用,以及细节处理)

  个人主页:欢迎大家光临——>沙漠下的胡杨

  各位大帅哥,大漂亮

 如果觉得文章对自己有帮助

 可以一键三连支持博主

 你的每一分关心都是我坚持的动力

 

 ☄: 本期重点:可变参数列表相关的简单讲解

  希望大家每天都心情愉悦的学习工作。

     


                        今天我们主题是可变参数列表              

可变参数列表简单介绍:

可变参数列表是怎么实现的

关于可变参数列表的原理解析

详情请见----->28张图讲解栈帧

关于可变参数的宏的简单介绍

如果函数传入字符型数据呢?

可变参数的注意事项

下期预告:

下期讲解可变参数列表实现的原理.


可变参数列表简单介绍:

首先我们最常使用的可变参数列表为 printf,scanf,那么什么是可变参数列表呢?

可变参数列表讲解《一》(简单介绍使用,以及细节处理)_第1张图片

 

我们可以对printf传入多个参数,在调用printf前你也不知道它会有几个参数,这个参数个数是可以改变的,这样的形式我们就叫做可变参数列表。

可变参数列表是怎么实现的

现在我们要算一些数的较大值,这些数的个数不知道,我们怎么办呢?

看下例子:

int GetMax(int num, ...)
{
	va_list arg;
	va_start(arg, num);
	int max = va_arg(arg, int);
	for (int i = 1; i < num; i++)
	{
		int cur = va_arg(arg, int);
		if (cur>max)
		{
			max = cur;

		}
	}
	va_end(arg);

	return max;
}


int main()
{
	int max = GetMax(5, 0x11, 0x22, 0x33, 0x44, 0x55);
	printf("max = %x\n", max);

	return 0;
}

 可变参数是通过 va_list 和 va_strat 和 va_end 和 va_arg 共同完成的。

首先 va_list 就是定义一个可以访问可变参数的变量是 char* 类型的。

va_start是使arg指向可变参数的部分。

va_arg是根据类型来回去可变参数列表的第一个数据

va_end使用完毕后使arg置为NULL,防止野指针的问题。

这是简单的使用和流程。

关于可变参数列表的解析

首先我们在函数栈帧那节课已经说过,关于函数栈帧的临时变量的拷贝和形参实例化的东西

详情请见----->28张图讲解栈帧

那我们就直接进入调试上述的代码:

可变参数列表讲解《一》(简单介绍使用,以及细节处理)_第2张图片

 通过调试我们看出了,其实在传递参数是,这个形参拷贝了在main函数战帧和GetMax函数战帧中间的。如图所示:

可变参数列表讲解《一》(简单介绍使用,以及细节处理)_第3张图片

 在战帧讲解时我们还说啦,这些形参实例化时地址是连续的,所以我们如果知道 5 这个变量的地址,我们就可以访问到所有的参数了,就不用说所有参数进行传入啦。

关于可变参数的宏的简单介绍

首先va_list 是一个char*类型的指针

可变参数列表讲解《一》(简单介绍使用,以及细节处理)_第4张图片

 

va_strat 和va_arg和va_end是宏

可变参数列表讲解《一》(简单介绍使用,以及细节处理)_第5张图片

简单分析GetMax函数

int GetMax(int num, ...)
{
	va_list arg;
	va_start(arg, num);
	int max = va_arg(arg, int);
	for (int i = 1; i < num; i++)
	{
		int cur = va_arg(arg, int);
		if (cur>max)
		{
			max = cur;

		}
	}
	va_end(arg);

	return max;
}

代码如上,首先在GetMax中,我们是传入啦一个有效参数的,所以我们用 va_list 创建的变量arg,使 va_start 通过 va_arg 和传进来的 num 共同让其指向了0x11。

可变参数列表讲解《一》(简单介绍使用,以及细节处理)_第6张图片

 可变参数列表讲解《一》(简单介绍使用,以及细节处理)_第7张图片

因为在内存中的这些数值是连续的,所以我们可以通过 va_start 找到剩下的值。

那么怎么找到呢?

int max = va_arg(arg,int)是通过 va_start 中 arg的起始位置,然后在向后访问 int 型的字节。

最后使用 va_end 防止出现野指针的问题。

这样我们就完成了对可变参数部分的访问。

如果函数传入字符型数据呢?

char GetMax(int num, ...)
{
	va_list arg;
	va_start(arg, num);
	int max = va_arg(arg, int);
	for (int i = 1; i < num; i++)
	{
		int cur = va_arg(arg, int);
		if (cur>max)
		{
			max = cur;

		}
	}
	va_end(arg);

	return max;
}


int main()
{
	char a = 'a';
	char b = 'b';
	char c = 'c';
	char d = 'd';
	char e = 'e';

	char max = GetMax(5, a, b, c, d, e);
	
	printf("max = %c\n", max);

	return 0;
}

代码如上:我们只是把参数变成了字符型,并把返回值变为char类型:

可变参数列表讲解《一》(简单介绍使用,以及细节处理)_第8张图片

 首先我们仔细观察的话会发现这个movsx是什么呢和平常mov不一样?

movsx:是带符号扩展,简单讲就是整形提升,把短整型提升为 int。

所以我们通过 va_arg 向后访问时会接着访问4个字节大小的内存来进行读取。

这样我们就能在整形提升后接着按照四字节读取。

可变参数的注意事项

1. 可变参数必须是从头到尾访问的,如果访问了几个变量后,想终止是可以的,但是想直接访问列表中间的参数是不可以的,必须从头访问。

2.可变参数列表使用时必须要有一个命名参数,如果连一个命名参数都没有的话,那么是无法使用的。

3.这些我们使用的宏是无法直接判断实际存在参数的个数的,所以使用时要么传入个数,要么通过一些其他的方式,比如printf中是通过格式控制形式来判断参数个数的。

4.宏也是无法判断参数类型的。所以也需要判断传入的类型。

5.如果va_arg中使用错误的类型将导致不可预告的错误。因为va_arg中是判断一次读取的字节数的,如果传入的类型错误,那么读取字节错误后果将不可预知。

下期预告:

下期讲解可变参数列表的实现原理.

下期更精彩哦 ~!

你可能感兴趣的:(函数栈帧的创建,c语言)