C语言中有些函数使用可变参数,比如常见的int printf( const char* format, ...),第一个参数format是固定的,其余的参数的个数和类型都不固定。

C语言用va_start等宏来处理这些可变参数。这些宏看起来很复杂,其实原理挺简单,就是根据参数入栈的特点从最靠近第一个可变参数的固定参数开始,依次获取每个可变参数的地址。下面我们来分析这些宏。

在stdarg.h头文件中,针对不同平台有不同的宏定义,我们选取X86平台下的宏定义:

typedef char *  va_list;

#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

#define va_start(ap,v)  ( ap = (va_list)&v + _INTSIZEOF(v) )

#define va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

#define va_end(ap)      ( ap = (va_list)0 )

_INTSIZEOF(n)宏是为了考虑那些内存地址需要对齐的系统,从宏的名字来应该是跟sizeof(int)对齐。一般的sizeof(int)=4,也就是参数在内存中的地址都为4的倍数。比如,如果sizeof(n)在1-4之间,那么_INTSIZEOF(n)=4;如果sizeof(n)在5-8之间,那么_INTSIZEOF(n)=8。

为了能从固定参数依次得到每个可变参数,va_start,va_arg充分利用下面两点:

1. C语言在函数调用时,先将最后一个参数压入栈

2. X86平台下的内存分配顺序是从高地址内存到低地址内存

高位地址

第N个可变参数

。。。

第二个可变参数

第一个可变参数      ? ap

固定参数           ? v

低位地址

由上图可见,v是固定参数在内存中的地址,在调用va_start后,ap指向第一个可变参数。这个宏的作用就是在v的内存地址上增加v所占的内存大小,这样就得到了第一个可变参数的地址。

接下来,可以这样设想,如果我能确定这个可变参数的类型,那么我就知道了它占用了多少内存,依葫芦画瓢,我就能得到下一个可变参数的地址。

让我再来看看va_arg,它先ap指向下一个可变参数,然后减去当前可变参数的大小即得到当前可变参数的内存地址,再做个类型转换,返回它的值。

要确定每个可变参数的类型,有两种做法,要么都是默认的类型,要么就在固定参数中包含足够的信息让程序可以确定每个可变参数的类型。比如,printf,程序通过分析format字符串就可以确定每个可变参数大类型。

最后一个宏就简单了,va_end使得ap不再指向有效的内存地址。

看了这几个宏,不禁让我再次感慨,C语言太灵活了,而且代码可以写得非常简洁,虽然有时候让人看得不是很明白,但是一旦明白 过来,你肯定会为它击掌叫好!

其实在varargs.h头文件中定义了UNIX System V实行的va系列宏,而上面在stdarg.h头文件中定义的是ANSI C形式的宏,这两种宏是不兼容的,一般说来,我们应该使用ANSI C形式的va宏。

c语言可变入参中的每个参数的类型可以不同,编程入门:浅谈C语言的可变参数相关推荐

  1. C语言布尔类型占几个字节,浅谈C语言中的布尔(bool)类型

    我们知道在C++里有专门的bool类型,用来表示真或假.但是在C语言里没有这样的类型(至少我是一直这么认为的),表达式的值0为假,非0为真.所以条件判断语句( if(-).while(-) )非常灵活 ...

  2. c语言指针很危险,浅谈C语言中指针使用不当的危险性.doc

    浅谈C语言中指针使用不当的危险性.doc 第 19 卷 Vol . 19 第 2 期 No . 2 洛阳师专学报 Journal of Luoyang Teachers College 2000 年 ...

  3. c程序语言的常量变量和标识符,浅谈C语言中的常量与变量.pdf

    课程教育研究 CourseEducationResearch 2014年4月 上旬刊 教学.信息 浅谈C语言中的常量与变量 刘 星 (青 岛工学院商学院 山东 青岛 266300) [摘要]在任何一种 ...

  4. c语言弱符号与函数指针,浅谈C语言中的强符号、弱符号、强引用和弱引用【转】...

    首先我表示很悲剧,在看<程序员的自我修养--链接.装载与库>之前我竟不知道C有强符号.弱符号.强引用和弱引用.在看到3.5.5节弱符号和强符号时,我感觉有些困惑,所以写下此篇,希望能和同样 ...

  5. c语言函数调用参数调用的太少,浅谈C语言函数调用参数压栈的相关问题

    参数入栈的顺序 以前在面试中被人问到这样的问题,函数调用的时候,参数入栈的顺序是从左向右,还是从右向左.参数的入栈顺序主要看调用方式,一般来说,__cdecl 和__stdcall 都是参数从右到左入 ...

  6. 浅谈C语言内存(栈)

    浅谈C语言内存 文章目录 浅谈C语言内存 内存分配 栈 斐波纳契数列 内存分配 在C语言中内存分别分为栈区(stack).堆区(heap).未初始化全局数据区.已初始化全局数据区.静态常量区(stat ...

  7. c语言函数参数压栈,函数调用压栈 浅谈C语言函数调用参数压栈的相关问题

    想了解浅谈C语言函数调用参数压栈的相关问题的相关内容吗,在本文为您仔细讲解函数调用压栈的相关知识和一些Code实例,欢迎阅读和指正,我们先划重点:函数调用压栈,下面大家一起来学习吧. 参数入栈的顺序 ...

  8. c语言 去掉双引号_技术分享|浅谈C语言陷阱和缺陷

    良好的软件架构.清晰的代码结构.掌握硬件.深入理解C语言是防错的要点,人的思维和经验积累对软件可靠性有很大影响.C语言诡异且有种种陷阱和缺陷,需要程序员多年历练才能达到较为完善的地步.软件的质量是由程 ...

  9. 浅谈go语言交叉编译

    浅谈go语言交叉编译 基础 cgo cgo设置编译和链接参数 静态库和动态库 静态库 动态库 静态编译 cgo的内部连接和外部连接 internal linking external linking ...

最新文章

  1. C语言读取bmp图像并做简单显示
  2. 什么是PRD、MRD与BRD?
  3. 18.移动架构数据库升级解决方案
  4. 如何禁用UITableView选择?
  5. 关于Exchange System Attendant服务
  6. apache 访问控制
  7. 删除数据oracle,oracle删除数据
  8. mysql var目录很快_mysql的这些坑你踩过吗?快来看看怎么优化mysql
  9. ARM交叉编译工具链
  10. Java零基础到进阶(真的零基础,也可以当笔记看~)
  11. 关闭WPS右键菜单,以及热点,广告弹窗推送
  12. 今天我们来聊一个很高级的话题:如何设计一个大规模远程命令执行系统
  13. 关于网络直播营销活动监管中的《广告法》
  14. 【精华】超详细的Win10安装步骤,菜鸟福音
  15. Error Client wants topic A to have B, but our version has C. Dropping connection.
  16. 安静!听听AI眼中岛国老师的声音~
  17. C语言-文件操作-文件打开方式
  18. 联想拯救者2020R7000双系统装机记录_自用
  19. 在vsc中运行html
  20. Python3,我用这种方式讲解python模块,80岁的奶奶都说能理解。建议收藏 ~ ~

热门文章

  1. 谷歌浏览器linux太耗电,Chrome浏览器太耗电?微软出招
  2. php stdin是什么意思,php:// input和php:// stdin之间有什么区别?
  3. mysql 事务 for update_mysql事务,select for update,及数据的一致性处理
  4. 32要烧写3个bin文件_入门教程3:如何给ESP8266烧录Gagent固件,快速接入机智云实现透传功能...
  5. python1~10阶乘_小练习 python3 阶乘运算
  6. spi四种工作模式时序图_SPI总线协议及SPI时序图详解
  7. MYSQL数据库VALUES_MYSQL入门大全来啦!
  8. osg::PositionAttitudeTransform旋转物体
  9. mysql高效sql语句_高效SQL优化 非常好用的SQL语句优化34条
  10. 【SIS-OAS 1.52.0】【C03-测试报告】常规版本回归测试报告-------回归测试报告模板...