谈到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) - 1) / 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 */
从代码中可以看到,里面编译器的版本以及相关的大量宏定义
第5行: #ifdef __GNUC__
作用是条件编译,__GNUC__为GCC中定义的宏。GCC的版本,为一个整型值。如果你需要知道自己的程序是否被GCC编译,可以简单的测试一下__GNUC__,假如你代码需要运行在GCC某个特定的版本下,那么你就要小心了,因为GCC的主要版本在增加,如果你想定义宏的方式直接实现控制,你可以写如下的代码(参见伯克利大学网站):
/* 测试 GCC > 3.2.0 ? */ #if __GNUC__ > 3 || \ (__GNUC__ == 3 && (__GNUC_MINOR__ > 2 || \ (__GNUC_MINOR__ == 2 && \ __GNUC_PATCHLEVEL__ > 0))
你还可以使用下面一个类似的方法:
#define GCC_VERSION (__GNUC__ * 10000 \ + __GNUC_MINOR__ * 100 \ + __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) - 1) / 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 )
相关推荐
### 《Unix网络编程》源代码分析 #### 一、简述 《Unix网络编程》是一本详尽介绍Unix环境下网络编程技术的经典书籍,书中包含了大量实用的代码示例,帮助读者深入理解网络编程的核心原理与实践技巧。本文将重点...
7. 其他功能:还有如`assert.h`头文件中的`assert`宏用于调试,`stdarg.h`支持可变参数列表,`setjmp.h`和`longjmp`用于非局部跳转等。 通过研究C标准库的源代码,你可以深入了解函数如何实现、错误处理如何进行,...
在深入研究源代码时,我们可能会遇到一些复杂的实现,如`stdio.h`中的缓冲区管理,`stdlib.h`中的内存分配器,或`math.h`中的浮点数运算优化。这些实现通常涉及操作系统接口、内存模型以及编译器特性,对理解计算机...
函数头文件通常以`.h`为扩展名,用于在源代码中引入所需的函数、常量、数据类型等,确保编译器知道如何处理这些元素。本资源“C语言函数头文件大全”提供了一个全面的参考,包含了C语言常用的标准库函数和一些常见的...
通过分析这些源代码,我们可以看到如何实现标准库中定义的功能,比如`printf`内部是如何处理格式化的,`malloc`是如何管理内存的,以及`strcat`是如何合并字符串的。此外,源代码也可能包含了一些测试用例,帮助我们...
这个名为"电子-C标准库源代码.zip"的压缩包文件包含的是C标准库的源代码,对于深入理解和学习C语言,以及计算机科学的基本原理具有极大的价值。 C标准库主要由ISO/IEC 9899标准定义,通常被称为C99或C11标准,这些...
自定义头文件通常以`.h`为扩展名,通过`#include`指令在源代码中引入。 例如,`myfunc.h`可能包含如下内容: ```c #ifndef MYFUNC_H #define MYFUNC_H int add(int a, int b); void print_array(int arr[], int ...
7. `<stdarg.h>`:变长参数头文件,支持可变参数列表的函数,如`printf`的变体`vprintf`。 8. `<float.h>`:浮点数特性头文件,定义了浮点数相关的常量,如`FLT_MAX`。 9. `<limits.h>`:整数类型限制头文件,定义...
在本文档中,我们将深入分析实现泊松曲面重建算法的源代码 `PoissonRecon.cpp`,理解其核心思想与具体实现细节。 #### 核心知识点详解 ##### 1. 引入头文件与平台兼容性处理 ```c++ 1#include<stdio.h> 2#include...
- **`stdarg.h`**:用于可变参数列表的函数。 - **`graphics.h`**:用于图形输出的库,通常与Borland C++编译器配套使用。 - **`string.h`**:提供字符串操作函数,如字符串复制、比较等。 - **`ctype.h`**:字符...
在C语言中,头文件的作用是为源代码提供预编译的声明,使得编译器在处理源代码时能够知道函数的存在、参数类型以及变量的定义等信息。ISOC头文件通常以`.h`为后缀,例如`<stdio.h>`、`<stdlib.h>`、`<string.h>`等,...
这些头文件通过`#include`指令被引入到源代码中,以提供必要的功能。本篇文章将深入探讨C和C++中常用的头文件,以及它们提供的函数和关键字的使用。 首先,`<assert.h>`头文件提供了`assert()`宏,它用于断言程序中...
例如,要使用<stdio.h>库中的函数,需要在源代码中包含相应的头文件: ```c #include <stdio.h> ``` 然后就可以使用库中的函数,比如打印"Hello, World!": ```c #include <stdio.h> int main() { printf(...
在日志实现中,`glog.cpp`和`glog.h`可能是包含实际日志功能实现和接口定义的源代码文件。`glog.h`将定义日志函数的原型,而`glog.cpp`则会实现这些函数的逻辑。通常,日志函数会有一个固定的参数,比如日志级别(如...
7. 其他:`<stdarg.h>`支持可变参数列表,`<float.h>`定义了浮点数相关的常量和宏,`<limits.h>`定义了各种类型的最小值和最大值。 值得注意的是,由于本地化和多字节字符集(如Unicode)的功能在C语言标准库中不是...
3. stdarg.h:这是一个标准的C库头文件,包含了stdarg宏,用于处理可变参数列表。虽然不是StrSafe库的一部分,但它可能在使用某些StrSafe函数时需要,特别是那些接受可变数量参数的函数,如vprintf系列。 4. strsafe...