stdarg.h以宏的形式定义变量列表- -va_list,va_start,vava_arg,_arg,va_end

一、stdarg.h原文
va在这里是variable-argument(可变参数)的意思

#ifndef _STDARG_H 
#define _STDARG_H 
typedef char *va_list; // 定义 va_list 是一个字符指针类型
#define _va_rounded_size(TYPE)(((sizeof(TYPE) + sizeof(int) - 1)/sizeof(int)) *sizeof(int)) 
#ifndef __spare__
#define va_start(AP, LASTARG)
   (AP = ((char *) &(LASTARG) + va_rounded size(LASTARG)))
#else
#define va_start(AP, LASTARG)
(__builtin_saveregs(), 
   AP = ((char *) &(LASTARG) + _va_rounded_size(LASTARG)))
#endif  
void va_end (va_list); //void va_end (va_list);是声明,
//va_end在 gnulib 中定义,在有些代码中va_end定义为:#define   va_end(ap)  ( ap = (va_list)0 )
#define va_end(AP) //将函数va_end作成宏        
#define va_arg(AP,TYPE)  
(AP += va_rounded_size (TYPE), 
*((TYPE *)(AP -  va_rounded_size(TYPE)))) 
#endif 

可以整理成宏if嵌套:

#ifndef _STDARG_H 
        #define _STDARG_H 
        typedef char *va_list; 
        #define _va_rounded_size(TYPE)(((sizeof(TYPE) + sizeof(int) - 1)/sizeof(int)) *sizeof(int)) //在 C 语言中,sizeof() 是一个判断数据类型或者表达式长度的运算符。
        
        
        #ifndef __spare__
                #define  va_start(AP, LASTARG)  (AP = ((char *) &(LASTARG) + va_rounded size(LASTARG)))
        #else
-               #define va_start(AP, LASTARG)  (__builtin_saveregs(),AP = ((char *) &(LASTARG) + _va_rounded_size(LASTARG)))
        #endif  
        
        
        void va_end (va_list); //void va_end (va_list);是声明,
        //va_end在 gnulib 中定义,在有些代码中va_end定义为:#define   va_end(ap)  ( ap = (va_list)0 )
        #define va_end(AP) //将函数va_end作成宏        
        #define  va_arg(AP,TYPE)  (AP += va_rounded_size (TYPE), *((TYPE *)(AP -  va_rounded_size(TYPE)))) 
#endif 

二 在实际情景中分析其中的宏

#include<stdio.h>
#include<stdlib.h>
#include<stdarg.h>
 
int add(int number, ...)//...代表多少个参数都可以。第一个add函数参数n指定了变参数列表参数的个数
{
	va_list v;//定义一个字符指针用于保存可以变长参数的列表
	va_start(v, number);//保存n之后的所有参数
	for (int i = 0; i < number; i++)
	{
		int data = va_arg(v, int);
		printf("%d\n", data);
	}
	va_end(v);//释放列表
 
	return 0;
}
void main()
{
    int a=3;
    int one=1;
    int two=2;
    int three=3;
    add(a,one,two,three);
    add(3,1,2,3);
}

输出
在这里插入图片描述
(1)__builtin_saveregs():
函数builtin saveregs()是在gcc的库程序libgcc2. c中定义的,用于保存寄存器。

(2)_va_rounded_size():

#define  _va_rounded_size(TYPE)  (((sizeof(TYPE) + sizeof(int) - 1)/sizeof(int)) *sizeof(int)) //type类型round圆形的

#define M (a+b)
......
......
s=M*M;
//上例程序中首先进行带参数宏定义,定义M来替代表达式(a+b),在 s= M * M 中作了宏调用。在预处理时经宏展开后该语句变为: S=(a+b)*(a+b)

此情此景:TYPE为int,所以#define _va_rounded_size(int) (((4+4-1)/4)x4),即_va_rounded_size宏展开为(((4+4-1)/4)x4),即返回4
(3)va_start():

#define  va_start(AP, LASTARG)  (AP = ((char *) &(LASTARG) + va_rounded size(LASTARG)))

此情此景:va_start宏展开为v=number+4,即置v为number+4,返回v(v要先定义)。此时v指向可变参数列表中第一个参数
作用:开始获取可变参数列表中的第一个参数(…里面的第一个),也就是跳过第一个add函数参数n
(4)va_arg():

 #define  va_arg(AP,TYPE)  (AP += va_rounded_size (TYPE), *((TYPE *)(AP -  va_rounded_size(TYPE))))

此情此景:va_arg宏展开为v=v+4,即置v为v+4,返回*((TYPE *)(v-4)
作用:循环使用,可循环获取到可变参数列表中的参数,v指向下一个参数地址,返回的则是当前参数地址
(5)va_end():

#define  va_end(AP)  ( AP=(va_list)0 )//va_end在 gnulib 中定义,在有些代码中va_end最终是宏:#define  va_end(AP)  ( AP=(va_list)0 )

此情此景:va_end宏展开为(char*)(v=0),即置v为0,返回0。
作用:用于防止出现野指针

三补充仅作为了解
具体去看:https://www.cnblogs.com/LyShark/p/12730393.html
定义并使用有参函数: 我们给函数传递些参数,然后分析其反汇编代码,观察代码的展示形式.


```c
#include <stdio.h>

int Function(int x,float y,double z)
{
	if (x = 100)
	{
		x = x + 100;
		y = y + 100;
		z = z + 100;
	}
	return (x);
}

int main(int argc, char* argv[])
{
	int ret = 0;
	ret = Function(100, 2.5, 10.245);
	printf("返回值: %d\n", ret);
	return 0;
}

下方的反汇编代码就是调用函数ret = Function()的过程,该过程中可看出压栈顺序遵循的是从后向前压入的.

0041145E | C745 F8 00000000         | mov dword ptr ss:[ebp-0x8],0x0                       | main.c:17
00411465 | 83EC 08                  | sub esp,0x8                                          | main.c:18
00411468 | F2:0F1005 70584100       | movsd xmm0,qword ptr ds:[<__real@40247d70a3d70a3d>]  | 将10.245放入XMM0寄存器
00411470 | F2:0F110424              | movsd qword ptr ss:[esp],xmm0                        | 取出XMM0中内容,并放入堆栈
00411475 | 51                       | push ecx                                             |
00411476 | F3:0F1005 68584100       | movss xmm0,dword ptr ds:[<__real@40200000>]          | 将2.5放入XMM0
0041147E | F3:0F110424              | movss dword ptr ss:[esp],xmm0                        | 同理
00411483 | 6A 64                    | push 0x64                                            | 最后一个参数100
00411485 | E8 51FDFFFF              | call 0x4111DB                                        | 调用Function函数
0041148A | 83C4 10                  | add esp,0x10                                         |
0041148D | 8945 F8                  | mov dword ptr ss:[ebp-0x8],eax                       | 将返回值压栈
00411490 | 8BF4                     | mov esi,esp                                          | main.c:19
00411492 | 8B45 F8                  | mov eax,dword ptr ss:[ebp-0x8]                       |
00411495 | 50                       | push eax                                             |
00411496 | 68 58584100              | push consoleapplication1.415858                      | 415858:"返回值: %d\n"
0041149B | FF15 14914100            | call dword ptr ds:[<&printf>]                        | 输出结果
004114A1 | 83C4 08                  | add esp,0x8                                          |

压栈完成以后我们可以继续跟进call 0x4111DB这个关键CALL,此处就是运算数据的关键函数,跟进去以后,可发现其对浮点数的运算,完全是依靠XMM寄存器实现的.

004113F1 | 8945 08                  | mov dword ptr ss:[ebp+0x8],eax                       |
004113F4 | F3:0F1045 0C             | movss xmm0,dword ptr ss:[ebp+0xC]                    | main.c:8
004113F9 | F3:0F5805 8C584100       | addss xmm0,dword ptr ds:[<__real@42c80000>]          |
00411401 | F3:0F1145 0C             | movss dword ptr ss:[ebp+0xC],xmm0                    |
00411406 | F2:0F1045 10             | movsd xmm0,qword ptr ss:[ebp+0x10]                   | main.c:9
0041140B | F2:0F5805 80584100       | addsd xmm0,qword ptr ds:[<__real@4059000000000000>]  |
00411413 | F2:0F1145 10             | movsd qword ptr ss:[ebp+0x10],xmm0                   |
00411418 | 8B45 08                  | mov eax,dword ptr ss:[ebp+0x8]                       | main.c:11
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值