谈到C语言中可变参数函数的实现(参见C语言中可变参数函数实现原理),有一个头文件不得不谈,那就是stdarg.h

本文从minix源码中的stdarg.h头文件入手进行分析:

 #ifndef _STDARG_H
#define _STDARG_H #ifdef __GNUC__
/* The GNU C-compiler uses its own, but similar varargs mechanism. */ typedef char *va_list; /* Amount of space required in an argument list for an arg of type TYPE.
* TYPE may alternatively be an expression whose type is used.
*/ #define __va_rounded_size(TYPE) \
(((sizeof (TYPE) + sizeof (int) - ) / sizeof (int)) * sizeof (int)) #if __GNUC__ < 2 #ifndef __sparc__
#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); /* Defined in gnulib */
#define va_end(AP) #define va_arg(AP, TYPE) \
(AP += __va_rounded_size (TYPE), \
*((TYPE *) (AP - __va_rounded_size (TYPE)))) #else /* __GNUC__ >= 2 */ #ifndef __sparc__
#define va_start(AP, LASTARG) \
(AP = ((char *) __builtin_next_arg ()))
#else
#define va_start(AP, LASTARG) \
(__builtin_saveregs (), AP = ((char *) __builtin_next_arg ()))
#endif void va_end (va_list); /* Defined in libgcc.a */
#define va_end(AP) #define va_arg(AP, TYPE) \
(AP = ((char *) (AP)) += __va_rounded_size (TYPE), \
*((TYPE *) ((char *) (AP) - __va_rounded_size (TYPE)))) #endif /* __GNUC__ >= 2 */ #else /* not __GNUC__ */ typedef char *va_list; #define __vasz(x) ((sizeof(x)+sizeof(int)-1) & ~(sizeof(int) -1)) #define va_start(ap, parmN) ((ap) = (va_list)&parmN + __vasz(parmN))
#define va_arg(ap, type) \
(*((type *)((va_list)((ap) = (void *)((va_list)(ap) + __vasz(type))) \
- __vasz(type))))
#define va_end(ap) #endif /* __GNUC__ */ #endif /* _STDARG_H */

stdarg.h源代码

从代码中可以看到,里面编译器的版本以及相关的大量宏定义

第5行: #ifdef __GNUC__
作用是条件编译,__GNUC__为GCC中定义的宏。GCC的版本,为一个整型值。如果你需要知道自己的程序是否被GCC编译,可以简单的测试一下__GNUC__,假如你代码需要运行在GCC某个特定的版本下,那么你就要小心了,因为GCC的主要版本在增加,如果你想定义宏的方式直接实现控制,你可以写如下的代码(参见伯克利大学网站):

/* 测试 GCC > 3.2.0 ? */
#if __GNUC__ > 3 || \
(__GNUC__ == && (__GNUC_MINOR__ > || \
(__GNUC_MINOR__ == && \
__GNUC_PATCHLEVEL__ > ))

你还可以使用下面一个类似的方法:

#define GCC_VERSION (__GNUC__ * 10000 \
+ __GNUC_MINOR__ * \
+ __GNUC_PATCHLEVEL__)
...
/*测试 GCC > 3.2.0 ?*/
#if GCC_VERSION > 30200

第8行: 使用typedef进行了一个声明:typedef char *va_list;

第14行:定义了用于编译器的内存对齐宏(参见C语言内存对齐详解(3)):

#define __va_rounded_size(TYPE)  \
(((sizeof (TYPE) + sizeof (int) - ) / sizeof (int)) * sizeof (int))

第17行:#if __GNUC__ < 2,进行GCC的版本判断,看当前版本是否大于2

第19行:#ifndef __sparc__ 可扩充处理器架构宏(以后再深入研究)

第20行:使得ap指向函数中的第一个无名参数的首地址的宏:

#define va_start(AP, LASTARG)                                           \
(AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG)))

第31行:

#define va_arg(AP, TYPE)                                                \
(AP += __va_rounded_size (TYPE), \
*((TYPE *) (AP - __va_rounded_size (TYPE))))

va_arg宏使得ap指向下一个参数,已经处理了内存对齐,其中参数的类型为TYPE

第48行:

void va_end (va_list);          /* Defined in gnulib */

定义在gnulib中,va_end 与va_start成对使用.在有些代码中定义为:

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

stdarg.h头文件源代码分析的更多相关文章

  1. stdarg.h头

    stdarg.h 头文件,主要目的是让函数可以接受可变参数. va_list :用来保存宏va_arg与宏va_end所需信息. va_start :使va_list指向起始的参数 va_arg :检 ...

  2. linux内核中链表代码分析---list.h头文件分析(一)【转】

    转自:http://blog.chinaunix.net/uid-30254565-id-5637596.html linux内核中链表代码分析---list.h头文件分析(一) 16年2月27日17 ...

  3. linux内核中链表代码分析---list.h头文件分析(二)【转】

    转自:http://blog.chinaunix.net/uid-30254565-id-5637598.html linux内核中链表代码分析---list.h头文件分析(二) 16年2月28日16 ...

  4. .h头文件和.c文件的作用和区别

    .h头文件和.c文件的作用和区别 在小工程中,.h的作用没有得到充分的使用,在大工程中才能充分体现出.h文件的作用. .h和.c文件都是代码.头文件好处有: 一:头文件便于共享,只需要添加一句“inc ...

  5. .h头文件、 .lib库文件、 .dll动态链接库文件之间的关系

    转自.h头文件. .lib库文件. .dll动态链接库文件之间的关系 h头文件作用:声明函数接口 dll动态链接库作用:含有函数的可执行代码 lib库有两种: (1)静态链接库(Static Liba ...

  6. jni.h头文件详解一

    1.jni.h头文件路径: /usr/lib/jvm/jdk_1.6.0_43/include/jni.h 2.jni.h头文件组成分析图: 3.下面通过上图进行分析讲解jni.h头文件. 一. jn ...

  7. .h头文件 .lib库文件 .dll动态库文件之间的关系

    .h头文件是编译时必须的,lib是链接时需要的,dll是运行时需要的. 附加依赖项的是.lib不是.dll,若生成了DLL,则肯定也生成 LIB文件.如果要完成源代码的编译和链接,有头文件和lib就够 ...

  8. 【C语言】中的stdbool.h头文件

    C语言中的stdbool.h头文件 一.相关基础知识 二.具体内容 Win7下安装的VS2015中的stdbool.h的位置为: F:\Program Files (x86)\Microsoft Vi ...

  9. .h头文件 .lib动态链接库文件 .dll 动态链接库

    (1).h头文件是编译时必须的,lib是链接时需要的,dll是运行时需要的. 附加依赖项的是.lib 不是.dll 若生成了DLL ,则肯定也生成 LIB文件 如果要完成源代码的编译和链接,有头文件和 ...

随机推荐

  1. spark1.4配置安装

    https://segmentfault.com/a/1190000004508993

  2. [ROM]HTC ThunderBolt 4.0.4 刷机教程

    Z大原帖:http://www.in189.com/thread-754076-1-1.html 精简版:http://www.in189.com/thread-807796-1-1.html 下载地 ...

  3. 5 JInja2模版(适用于Django和Flask)

    模版 在生产环节下,我们要把后端程序(其实就是python)计算出来的数据和html页面结合起来做,这个时候模版就派上大用处了. Flask下的模版---Jinja2 Jinja是日本寺庙的意思,并且 ...

  4. ExtJS中给Tree节点加click事件

    第一种: 直接通过TreePanel中的Config Option中的listener来添加,代码如下: var TreePan = new Ext.tree.TreePanel({ id: 'Tre ...

  5. mysql压缩版的安装教程

    1.     首先创建 my.ini,在mysql解压目录下的bin文件夹中新建一个名为 my.ini 的文件,内容为 [client] port=3306 default-character-set ...

  6. 关于移位运算符>>和<<

    首先,移位运算符有三种,其操作类型只支持:byte / short / char / int和long五种. << 左移运算符,表示将左边的操作数的二进制数据向左移动*位,移动后空缺位以0 ...

  7. iOS开发 frame 与 bounds 的区别与关系 转自隔叶黄莺

    frame和bounds是UIView中的两个属性(property). frame指的是:该view在父view坐标系统中的位置和大小.(参照点是父亲的坐标系统) bounds指的是:该view在本 ...

  8. 设置js同源

    1)设置 document.domain 成一样的就行了(前提是都是同一个顶级域名) 2)例如,域名1:news.xxx.com ,域名2:member.xxx.com,这时可以设置它们的 docum ...

  9. CMS4.0——后知后觉

    前言: 2016年底,自己作为参与者加入CMS3.0的改版中:2017年中,CMS4.0在经过一个月有余的时间,华丽丽的蜕变成现在大家喜闻乐见的:http://news.gangguwang.com/ ...

  10. C++ template —— 模板基础(一)

    <C++ Template>对Template各个方面进行了较为深度详细的解析,故而本系列博客按书本的各章顺序编排,并只作为简单的读书笔记,详细讲解请购买原版书籍(绝对物超所值).---- ...