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

2023-10-31 00:50

本文主要是介绍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

这篇关于stdarg.h以宏的形式定义变量列表- -va_list,va_start,vava_arg,_arg,va_end的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/311688

相关文章

把Python列表中的元素移动到开头的三种方法

《把Python列表中的元素移动到开头的三种方法》在Python编程中,我们经常需要对列表(list)进行操作,有时,我们希望将列表中的某个元素移动到最前面,使其成为第一项,本文给大家介绍了把Pyth... 目录一、查找删除插入法1. 找到元素的索引2. 移除元素3. 插入到列表开头二、使用列表切片(Lis

Django中的函数视图和类视图以及路由的定义方式

《Django中的函数视图和类视图以及路由的定义方式》Django视图分函数视图和类视图,前者用函数处理请求,后者继承View类定义方法,路由使用path()、re_path()或url(),通过in... 目录函数视图类视图路由总路由函数视图的路由类视图定义路由总结Django允许接收的请求方法http

python中列表应用和扩展性实用详解

《python中列表应用和扩展性实用详解》文章介绍了Python列表的核心特性:有序数据集合,用[]定义,元素类型可不同,支持迭代、循环、切片,可执行增删改查、排序、推导式及嵌套操作,是常用的数据处理... 目录1、列表定义2、格式3、列表是可迭代对象4、列表的常见操作总结1、列表定义是处理一组有序项目的

C++11范围for初始化列表auto decltype详解

《C++11范围for初始化列表autodecltype详解》C++11引入auto类型推导、decltype类型推断、统一列表初始化、范围for循环及智能指针,提升代码简洁性、类型安全与资源管理效... 目录C++11新特性1. 自动类型推导auto1.1 基本语法2. decltype3. 列表初始化3

Spring Boot中的路径变量示例详解

《SpringBoot中的路径变量示例详解》SpringBoot中PathVariable通过@PathVariable注解实现URL参数与方法参数绑定,支持多参数接收、类型转换、可选参数、默认值及... 目录一. 基本用法与参数映射1.路径定义2.参数绑定&nhttp://www.chinasem.cnbs

c++ 类成员变量默认初始值的实现

《c++类成员变量默认初始值的实现》本文主要介绍了c++类成员变量默认初始值,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录C++类成员变量初始化c++类的变量的初始化在C++中,如果使用类成员变量时未给定其初始值,那么它将被

Python中将嵌套列表扁平化的多种实现方法

《Python中将嵌套列表扁平化的多种实现方法》在Python编程中,我们常常会遇到需要将嵌套列表(即列表中包含列表)转换为一个一维的扁平列表的需求,本文将给大家介绍了多种实现这一目标的方法,需要的朋... 目录python中将嵌套列表扁平化的方法技术背景实现步骤1. 使用嵌套列表推导式2. 使用itert

C# 比较两个list 之间元素差异的常用方法

《C#比较两个list之间元素差异的常用方法》:本文主要介绍C#比较两个list之间元素差异,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1. 使用Except方法2. 使用Except的逆操作3. 使用LINQ的Join,GroupJoin

python3如何找到字典的下标index、获取list中指定元素的位置索引

《python3如何找到字典的下标index、获取list中指定元素的位置索引》:本文主要介绍python3如何找到字典的下标index、获取list中指定元素的位置索引问题,具有很好的参考价值,... 目录enumerate()找到字典的下标 index获取list中指定元素的位置索引总结enumerat

Python变量与数据类型全解析(最新整理)

《Python变量与数据类型全解析(最新整理)》文章介绍Python变量作为数据载体,命名需遵循字母数字下划线规则,不可数字开头,大小写敏感,避免关键字,本文给大家介绍Python变量与数据类型全解析... 目录1、变量变量命名规范python数据类型1、基本数据类型数值类型(Number):布尔类型(bo