PS:要转载请注明出处,本人版权所有。

PS: 这个只是基于《我自己》的理解,

如果和你的原则及想法相冲突,请谅解,勿喷。

前置说明

  本文作为本人csdn blog的主站的备份。(BlogID=032)

  本文发布于 2017-03-14 17:12:28,现用MarkDown+图床做备份更新。blog原图已丢失,使用csdn所存的图进行更新。(BlogID=032)

环境说明

  无

前言


  在几年前,由于兴趣需要,去研究了c的不定参数问题,当时由于太懒,没有记录任何资料,导致现在实际需要的时候,又要重新来过。呜呼哀哉,太烦了~~~~~~~

c/cpp不定参数


  首先,c不定参数所定义的宏的头文件在 stdarg.h中。在我的系统中在这里:/usr/lib/gcc/x86_64-linux-gnu/5/include/stdarg.h,源码我就不贴了,就简要分析几个重要宏的原理。

//va_list  //此宏依赖与不同的平台和操作系统。
//常用定义:(具体和编译器有关,其实了解透后,都差不多)
typedef char * __builtin_va_list;//(注意可能是这样定义的)
(or)
typedef void * __builtin_va_list;//(注意可能是这样定义的)
typedef __builtin_va_list __gnuc_va_list;
typedef __gnuc_va_list va_list; //_INTSIZEOF(n) //此宏是windows上定义的,目的是为了考虑兼容一些内存地址对齐的系统,n应该占多少字节,如:arm指令集是4字节对齐访问,Thumb指令集是24字节对齐访问.在linux上,gcc编译器会内部定义一个类似的宏,来代表对齐的字节数。其用法是为了根据一个确定的参数,来依次确定不定参数中的每一个参数。
#define _INTSIZEOF(n) \
( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
//一般在一个操作系统中,int类型所占的字节数即为当前对齐的字节数,一般为4或者8。关于此宏的详细分析,以下的分析来至于网上,带入sizeof(n) = 4,8即可明白:
#define _INTSIZEOF(n) ((sizeof(n) + x) & ~(x))
x = sizeof(int) - 1 = 3 = 0000 0000 0000 0011(b)
~x = 1111 1111 1111 1100(b) //va_start(v,l) //此宏获取的是可变参数中的第一个参数的地址
#define __builtin_va_start(v,l) \
( v = (va_list)&l + _INTSIZEOF(l) )//(注意可能是这样定义的),l的地址加上l地址所占的空间
#define va_start(v,l) __builtin_va_start(v,l) //va_arg(v,l)//此宏是使下一个可变参数的地址赋值给v,同时返回开始时v地址所指向的值,注意:此时v已经指向下一个参数地址,但是表达式返回的值是初始v的值,《《这里只需要搞清楚括号优先级就能够明白》》。
#define __builtin_va_arg(v,l) \
( *(l *)((v += _INTSIZEOF(l)) - _INTSIZEOF(l)) )//(注意可能是这样定义的)
#define va_arg(v,l) __builtin_va_arg(v,l) //va_end(v)//此宏处理va_list声明的一个指针,防止出现野指针。有始有终。
#define __builtin_va_end(v)\
( v = (va_list)0 )
#define va_end(v) __builtin_va_end(v)
来个例子(t.c)
#include <stdarg.h>
#include <stdio.h> void ttt(char * fuk,...); int main(int argc, char * argv[]){
ttt("fuk",2333,"this is 23333333333333333");
return 0;
}
void ttt(char * fuk,...){ va_list arg;
int a;
char *b;
va_start(arg,fuk);
a = va_arg(arg,int);
b = va_arg(arg,char *);
va_end(arg); printf("%d,%s\n",a,b);
}

编译

gcc t.c
./a.out

后记


最后的注意:

  1. 变参函数必须有一个以上的指定参数
  2. 变参的《《类型》》必须通过一定方式已知(如格式化字符串,"%d,%s"等等),否则就是定死的
  3. 变参的《《个数》》也必须通过一定方式已知(如格式化字符串,"%d,%s"等等),否则就是定死的
  4. c的函数参数入栈方式是从右到左,栈的地址一般是从高到底。

参考文献


打赏、订阅、收藏、丢香蕉、硬币,请关注公众号(攻城狮的搬砖之路)

PS: 请尊重原创,不喜勿喷。

PS: 要转载请注明出处,本人版权所有。

PS: 有问题请留言,看到后我会第一时间回复。

C 可变参数函数分析(va_start,va_end,va_list...)的更多相关文章

  1. va_start可变参数函数

    void va_start(va_list ap, last); //变参起始地址 type va_arg(va_list ap, type); //下一个参数的地址 void va_end(va_l ...

  2. C语言可变参数函数实现原理

    一.可变参数函数实现原理 C函数调用的栈结构: 可变参数函数的实现与函数调用的栈结构密切相关,正常情况下C的函数参数入栈规则为__stdcall, 它是从右到左的,即函数中的最右边的参数最先入栈. 本 ...

  3. c语言可变参数函数

    c语言支持可变参数函数.这里的可变指,函数的参数个数可变. 其原理是,一般情况下,函数参数传递时,其压栈顺序是从右向左,栈在虚拟内存中的增长方向是从上往下.所以,对于一个函数调用 func(int a ...

  4. C语言学习020:可变参数函数

    顾名思义,可变参数函数就是参数数量可变的函数,即函数的参数数量是不确定的,比如方法getnumbertotal()我们即可以传递一个参数,也可以传递5个.6个参数 #include <stdio ...

  5. C语言中可变参数函数实现原理

    C函数调用的栈结构 可变参数函数的实现与函数调用的栈结构密切相关,正常情况下C的函数参数入栈规则为__stdcall, 它是从右到左的,即函数中的最右边的参数最先入栈.例如,对于函数: void fu ...

  6. C可变参数函数 实现

    转自:http://blog.csdn.net/weiwangchao_/article/details/4857567 C函数要在程序中用到以下这些宏: void va_start( va_list ...

  7. c可变参数函数

    C函数要在程序中用到以下这些宏: <pre lang="c" escaped="true">void va_start( va_list arg_p ...

  8. 【转】C/C++中可变参数函数的实现

    转自:http://www.cnblogs.com/cylee025/archive/2011/05/23/2054792.html 在C语言的stdarg.h头文件中提供了三个函数va_start, ...

  9. C语言可变参数函数的编写

    1. 引言 C语言我们接触的第一个库函数是 printf(“hello,world!”);其参数个数为1个. 然后,我们会接触到诸如: printf(“a=%d,b=%s,c=%c”,a,b,c);此 ...

  10. 可变参数函数(stdarg.h)的使用

    2013/5/3记录: stdarg.h是C语言中C标准函数库的头文件,stdarg是由standard(标准) arguments(参数)简化而来,主要目的为让函数能够接收可变参数.   stdar ...

随机推荐

  1. Arduino语言基础(萌新)

    Arduino语言基础(萌新) Arduino语言注解Arduino语言是建立在C/C++基础上的,其实也就是基础的C语言,Arduino语言只不过把AVR单片机(微控制器)相关的一些参数设置都函数化 ...

  2. Windows平台安装Oracle11.2.0.4客户端报错INS-30131

    之前为解决EXP-00003错误给出了安装Oracle11.2.0.4的解决方案,自己测试是没问题的,客户自己安装反馈遇到报错INS-30131,MOS有一篇文章: E1: DB: Error INS ...

  3. 深入浅出 testing-library

    我们是袋鼠云数栈 UED 团队,致力于打造优秀的一站式数据中台产品.我们始终保持工匠精神,探索前端道路,为社区积累并传播经验价值. 本文作者:佳岚 The more your tests resemb ...

  4. RDS for MySQL并发性能测试

    最近由于工作需要,需要对阿里云数据库-RDS for MySQL进行性能测试,通过MySQL自带的mysqlslap工具可以进行并发性能测试,但是输出显示总感觉有问题,所以就萌生想法自己开发代码通过J ...

  5. IDEA中使用ChatGPT

    IDEA中使用ChatGPT 在IDEA中安装ChatGPT插件,可以帮助写基础逻辑代码,提高工作效率和学习效率,有兴趣可以玩一下. 插件名为 Bito. 1. 什么是Bito Bito是一款在Int ...

  6. .NET应用程序7种最常见的性能问题及其解决方案

    译者注:这篇文章依然是介绍.NET Framework框架下的性能问题排查,可能并不直接适用于.NET Core,但有时也能提供一些参考.   .NET应用程序7种最常见的性能问题及其解决方案 原文地 ...

  7. numpy数组初始化方法总结

    1 使用list初始化 a=np.array([[1,2,3],[4,5,6]],dtype='float32') #a=[[1. 2. 3.],[4. 5. 6.]] 2 赋值与复制 (1)赋值 a ...

  8. springboot中前端ajax如何给controller提交数组参数?

    说明 我有个需求,前端批量添加一堆商品明细.也就是说会有一个商品ID,然后一堆商品明细,多行. 如此一来,针对后端接口肯定是要以数组或列表方式接收这个商品明细数组了. 前端代码 关键地方在于以form ...

  9. 深入理解Go语言(03):scheduler调度器 - 基本介绍

    一:什么是调度 平常我们在生活中会有哪些调度的例子呢?比如十字路口的红绿灯,它就是一种调度系统.在交通十字路口,每个路口上多多少少有一些车辆,为了限制这些车辆不随意行驶,就建起了红绿灯调度系统.红绿灯 ...

  10. 文心一言 VS 讯飞星火 VS chatgpt (201)-- 算法导论15.2 6题

    六.用go语言,证明:对 n 个元素的表达式进行完全括号化,恰好需要 n-1 对括号. 文心一言,代码正常运行: 在Go语言中,证明对n个元素的表达式进行完全括号化恰好需要n-1对括号,可以通过数学归 ...