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

相关文章

Java中List转Map的几种具体实现方式和特点

《Java中List转Map的几种具体实现方式和特点》:本文主要介绍几种常用的List转Map的方式,包括使用for循环遍历、Java8StreamAPI、ApacheCommonsCollect... 目录前言1、使用for循环遍历:2、Java8 Stream API:3、Apache Commons

浅析Rust多线程中如何安全的使用变量

《浅析Rust多线程中如何安全的使用变量》这篇文章主要为大家详细介绍了Rust如何在线程的闭包中安全的使用变量,包括共享变量和修改变量,文中的示例代码讲解详细,有需要的小伙伴可以参考下... 目录1. 向线程传递变量2. 多线程共享变量引用3. 多线程中修改变量4. 总结在Rust语言中,一个既引人入胜又可

Python实现将实体类列表数据导出到Excel文件

《Python实现将实体类列表数据导出到Excel文件》在数据处理和报告生成中,将实体类的列表数据导出到Excel文件是一项常见任务,Python提供了多种库来实现这一目标,下面就来跟随小编一起学习一... 目录一、环境准备二、定义实体类三、创建实体类列表四、将实体类列表转换为DataFrame五、导出Da

Python中列表的高级索引技巧分享

《Python中列表的高级索引技巧分享》列表是Python中最常用的数据结构之一,它允许你存储多个元素,并且可以通过索引来访问这些元素,本文将带你深入了解Python列表的高级索引技巧,希望对... 目录1.基本索引2.切片3.负数索引切片4.步长5.多维列表6.列表解析7.切片赋值8.删除元素9.反转列表

java如何调用kettle设置变量和参数

《java如何调用kettle设置变量和参数》文章简要介绍了如何在Java中调用Kettle,并重点讨论了变量和参数的区别,以及在Java代码中如何正确设置和使用这些变量,避免覆盖Kettle中已设置... 目录Java调用kettle设置变量和参数java代码中变量会覆盖kettle里面设置的变量总结ja

Perl 特殊变量详解

《Perl特殊变量详解》Perl语言中包含了许多特殊变量,这些变量在Perl程序的执行过程中扮演着重要的角色,:本文主要介绍Perl特殊变量,需要的朋友可以参考下... perl 特殊变量Perl 语言中包含了许多特殊变量,这些变量在 Perl 程序的执行过程中扮演着重要的角色。特殊变量通常用于存储程序的

变量与命名

引言         在前两个课时中,我们已经了解了 Python 程序的基本结构,学习了如何正确地使用缩进来组织代码,并且知道了注释的重要性。现在我们将进一步深入到 Python 编程的核心——变量与命名。变量是我们存储数据的主要方式,而合理的命名则有助于提高代码的可读性和可维护性。 变量的概念与使用         在 Python 中,变量是一种用来存储数据值的标识符。创建变量很简单,

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

c++的初始化列表与const成员

初始化列表与const成员 const成员 使用const修饰的类、结构、联合的成员变量,在类对象创建完成前一定要初始化。 不能在构造函数中初始化const成员,因为执行构造函数时,类对象已经创建完成,只有类对象创建完成才能调用成员函数,构造函数虽然特殊但也是成员函数。 在定义const成员时进行初始化,该语法只有在C11语法标准下才支持。 初始化列表 在构造函数小括号后面,主要用于给

GNSS CTS GNSS Start and Location Flow of Android15

目录 1. 本文概述2.CTS 测试3.Gnss Flow3.1 Gnss Start Flow3.2 Gnss Location Output Flow 1. 本文概述 本来是为了做Android 14 Gnss CTS 的相关环境的搭建和测试,然后在测试中遇到了一些问题,去寻找CTS源码(/cts/tests/tests/location/src/android/locat