一、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