stdarg.h头文件源代码分析
谈到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头文件源代码分析的更多相关文章
- stdarg.h头
stdarg.h 头文件,主要目的是让函数可以接受可变参数. va_list :用来保存宏va_arg与宏va_end所需信息. va_start :使va_list指向起始的参数 va_arg :检 ...
- linux内核中链表代码分析---list.h头文件分析(一)【转】
转自:http://blog.chinaunix.net/uid-30254565-id-5637596.html linux内核中链表代码分析---list.h头文件分析(一) 16年2月27日17 ...
- linux内核中链表代码分析---list.h头文件分析(二)【转】
转自:http://blog.chinaunix.net/uid-30254565-id-5637598.html linux内核中链表代码分析---list.h头文件分析(二) 16年2月28日16 ...
- .h头文件和.c文件的作用和区别
.h头文件和.c文件的作用和区别 在小工程中,.h的作用没有得到充分的使用,在大工程中才能充分体现出.h文件的作用. .h和.c文件都是代码.头文件好处有: 一:头文件便于共享,只需要添加一句“inc ...
- .h头文件、 .lib库文件、 .dll动态链接库文件之间的关系
转自.h头文件. .lib库文件. .dll动态链接库文件之间的关系 h头文件作用:声明函数接口 dll动态链接库作用:含有函数的可执行代码 lib库有两种: (1)静态链接库(Static Liba ...
- jni.h头文件详解一
1.jni.h头文件路径: /usr/lib/jvm/jdk_1.6.0_43/include/jni.h 2.jni.h头文件组成分析图: 3.下面通过上图进行分析讲解jni.h头文件. 一. jn ...
- .h头文件 .lib库文件 .dll动态库文件之间的关系
.h头文件是编译时必须的,lib是链接时需要的,dll是运行时需要的. 附加依赖项的是.lib不是.dll,若生成了DLL,则肯定也生成 LIB文件.如果要完成源代码的编译和链接,有头文件和lib就够 ...
- 【C语言】中的stdbool.h头文件
C语言中的stdbool.h头文件 一.相关基础知识 二.具体内容 Win7下安装的VS2015中的stdbool.h的位置为: F:\Program Files (x86)\Microsoft Vi ...
- .h头文件 .lib动态链接库文件 .dll 动态链接库
(1).h头文件是编译时必须的,lib是链接时需要的,dll是运行时需要的. 附加依赖项的是.lib 不是.dll 若生成了DLL ,则肯定也生成 LIB文件 如果要完成源代码的编译和链接,有头文件和 ...
随机推荐
- 关于WSDL文件
endpoint Name是在Service节点中指定的. 而非是binding.binding节点只是绑定soapaction.
- PHP和Apache结合 Apache默认虚拟主机
- spark 修改分区(存储结果需要)
修改分区就行了val rdd1 = sc.parallelize(Array(1,2,3,4,5,6,7,8))# 查看分区数rdd1.partitions.length# 改成1个分区val rd ...
- [转帖]CentOS下iRedMail安装配置
CentOS下iRedMail安装配置 中文名为艾瑞得邮件,由 rhms 项目更名而来.是针对 Linux 设计的邮件服务器解决方案,是在操作系统安装好后使用的一套 shell 脚本,用于快速部署一套 ...
- kafka学习之-server.properties详细说明
http://blog.csdn.net/lizhitao/article/details/25667831 -- 参考文章 http://kafka.apache.org/documentatio ...
- linux将标准输出和标准错误输出都重定向到一个文件?
需求描述: 今天在写crontab,里面有标准输出和错误输出,之前使用的是 > /dev/null 2>&1 那这个意思也就等同于将标准输出和错误输出都输出到/dev/null中, ...
- repo_folder
-- Create table create table REPO_FOLDER ( UUID ) not null, VALID ) not null, CREATE_TIME ) not null ...
- java获取map中的最小KEY,最小VALUE
import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Map ...
- [Algorithm] Beating the Binary Search algorithm – Interpolation Search, Galloping Search
From: http://blog.jobbole.com/73517/ 二分检索是查找有序数组最简单然而最有效的算法之一.现在的问题是,更复杂的算法能不能做的更好?我们先看一下其他方法. 有些情况下 ...
- DOS 配置IP地址
@echo off :startIP set /p source=STATIC Y or N or E: echo source:%source% if "%source%" == ...