当前位置: 首页 > ds >正文

linux c可变参数va_start、va_end、va_arg、va_list

原文地址 http://daileinote.com/computer/c_base/13

va_start、va_end、va_arg、va_list 其实是宏定义,在不定参数函数中会用到。

在解释上面几个之前,我们先来做几件事。

 

确定栈增长方向

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>void test2();void test1(){int a = 1;printf("a %p\n", &a);test2();
}void test2(){int b = 1;printf("b %p\n", &b);
}int main(int argc, char *argv[]){test1();return 0;
}
[root@izj6cfw9yi1iqoik31tqbgz c]# ./a.out 
a 0x7ffc59d91b5c
b 0x7ffc59d91b3c

上面 test1 调用 test2,所以test1函数中的变量肯定比test2函数变量要先入栈,所以 变量a 先入栈。从输出的结果可以看出,a地址 > b地址。

得出结论先入栈的地址在高位,栈是向虚拟地址减少的方向增长(不同的环境可能不一样)。

 

确定函数参数入栈顺序

#include <stdio.h>void test(int a, int b, char *aa, char *bb, char aaa,char bbb){printf("%p %p %p %p %p %p\n", &a, &b, &aa, &bb, &aaa, &bbb);
}int main(int argc, char *argv[]){test(1,2,"沧浪水", "http://www.freecls.com",'a','b');return 0;
}
[root@izj6cfw9yi1iqoik31tqbgz c]# ./a.out 
0x7ffc985f6abc 0x7ffc985f6ab8 0x7ffc985f6ab0 0x7ffc985f6aa8 0x7ffc985f6aa4 0x7ffc985f6aa0

从输出可以看出 参数a 地址最大,所以参数a先入栈,所以函数参数是从左往右依次入栈。

 

了解va_start、va_end...原理

从上文我们可以看出,函数参数是依次压入栈的,大小会根据实际类型,最小为int型(从上面可以看出,char型也占用4个字节)。所以要实现可变参数函数,我们必须知道以下3个条件:

1.最后一个已知参数的地址
2.未知参数的个数
3.每个未知参数的类型

就可以通过指针移动,分别取得各个参数。下面内核3.16 的相关宏定义,有兴趣的朋友可以自己去研究。

#ifndef va_arg#ifndef _VALIST
#define _VALIST
typedef char *va_list;
#endif				/* _VALIST *//* Storage alignment properties */#define  _AUPBND                (sizeof (acpi_native_int) - 1)
#define  _ADNBND                (sizeof (acpi_native_int) - 1)/* Variable argument list macro definitions */#define _bnd(X, bnd)            (((sizeof (X)) + (bnd)) & (~(bnd)))
#define va_arg(ap, T)           (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))
#define va_end(ap)              (ap = (va_list) NULL)
#define va_start(ap, A)         (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))#endif				/* va_arg */

 

va_start、va_end...用法

在了解了原理之后,我们通过例子来讲解实际用法。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdarg.h>#define BUF_SIZE 500//未知类型,未知参数个数格式化
void errExit(int err_code, char *format, ...){char userMsg[BUF_SIZE];va_list argList;//这里的第二个参数为最后一个已知参数va_start(argList, format);vsnprintf(userMsg, BUF_SIZE, format, argList);va_end(argList);printf("%s", userMsg);exit(err_code);
}//已知参数个数,已知类型
//第一个参数用来指定后面参数个数
//后面的参数类型全部为int
void dosum(int sum, ...){va_list argList;va_start(argList, sum);int n = 0;int i = 0;for(; i < sum; i++){n = n + va_arg(argList, int);}va_end(argList);printf("sum:%d\n", n);
}int main(int argc, char *argv[]){dosum(4, 1,2,3,4);errExit(1, "%s %s\n", "hello", "freecls");
}
[root@izj6cfw9yi1iqoik31tqbgz c]# ./a.out 
sum:10
hello freecls

读者只要记住一点,想要实现可变参数函数,必须满足上面提到的3个条件,当然实现方式各种各样。比如 vsnprintf() 它是通过传入格式化字符串来确定参数个数以及参数类型的(下面代表3个参数,类型分别为 int、char *、char)。

"%d %s %c\n"

 

原文地址 http://daileinote.com/computer/c_base/13​​​​​​​

http://www.xdnf.cn/news/11149.html

相关文章:

  • 使用计算机的便利,allshare play:只要几个步骤立即使用这个非常便利的功能!...
  • adobe dreamweaver cs5序列号
  • Ubuntu 9.04安装教程(傻瓜版)
  • struts2通配符使用
  • 如何在VirtualBox虚拟机中安装XP系统? 转
  • Android签名机制及PMS中校验流程(雷惊风)
  • Cadence Allegro PCB设计88问解析(七) 之 Allegro位号反标OrCAD
  • Java版文本编辑器
  • socks5 运行几个小时后 端口10808不通了,ss5服务正常
  • Spring整合Quartz框架实现定时任务跑批(Maven完整版)
  • 超级详细的GitLab安装 与使用 【Gitlab添加组、创建用户和项目、权限管理】_gitlab群组
  • 探秘GPT:开启人工智能语言模型的新纪元
  • Yandex 邮箱添加
  • 麒麟短线王至尊版 软件及指标 应用!
  • Linux系统下安装部署Linux管理面板1panel
  • Win11系统提示找不到olecli32.dll文件的解决办法
  • web.xml中context-param的配置作用
  • dll和so文件区别与构成
  • 世界环保创业基金会简介
  • LDAP 目录服务器的现代化应用
  • 动态实现RelativeLayout,LinearLayout布局
  • Lenovo笔记本新版Veriface Pro(人像识别)软件介绍
  • InstallShield 12 制作安装包
  • anaconda安装gdal、Fiona、shapely、pyproj、geopandas
  • size mismatch: error 在搭建网络的时候
  • 使用JavaScript实现选项卡
  • java内存区域理解-初步了解
  • 洛谷 P2452 [SDOI2005] 屠龙传说-屠龙枪卷
  • 2022-04-30 Unity核心2——Sprite
  • 10种用于渗透测试的漏洞扫描工具有哪些_渗透测试和漏洞扫描区别_渗透漏洞分级工具