微博:http://weibo.com/u/2203007022           




 (1)      C语言可变參数

我们能够从C语言的printf得出可变參数的作用。printf函数的原型例如以下:

  1. int printf ( const char * format, ... );

通过使用可变个数參数,就是传入的參数个数是可变的,如printf须要依据format实參传入多个实參。

(2)      C语言可变參数的使用

以下一个函数myprintf是自己实现的比較简单的printf函数。不完整可是能够说明可变參数的使用方法。

  1. /*
  2. * Author: guojun07
  3. */
  4.  
  5. #include <stdio.h>
  6. #include <stdarg.h>
  7. #include <unistd.h>
  8. #include <string.h>
  9.  
  10. void myprintf(char *format, ...) {
  11. va_list ap;
  12. int pos = 0;
  13. int int_val = 0;
  14. float f_val;
  15. char buf[64];
  16. memset(buf, 0, 64);
  17. // 得到全部的參数放到下一个list中ap中
  18. va_start(ap, format);
  19. while (format[pos] != '\0') {
  20. // 推断'%'。表示要得到下一个參数
  21. if (format[pos] == '%') {
  22. pos ++;
  23. switch(format[pos]) {
  24. case 'd':
  25. case 'u':
  26. // 得到ap中的下一个參数
  27. int_val = va_arg(ap, int);
  28. sprintf(buf, "%d", int_val);
  29. // 将数据写到标准输出
  30. write(STDOUT_FILENO, buf, strlen(buf));
  31. memset(buf, 0, 64);
  32. pos ++;
  33. break;
  34. case 'f':
  35. // 得到ap中的下一个參数
  36. f_val = (float)va_arg(ap, double);
  37. sprintf(buf, "%f", f_val);
  38. // 将数据写到标准输出
  39. write(STDOUT_FILENO, buf, strlen(buf));
  40. memset(buf, 0, 64);
  41. pos ++;
  42. break;
  43. default:
  44. break;
  45. }
  46. } else {
  47. write(STDOUT_FILENO, &(format[pos]), 1);
  48. pos ++;
  49. }
  50. }
  51. }
  52.  
  53. int main(void){
  54. myprintf("this is a testing, i = %d, u = %u, f = %f\n", -1, 5, 0.2);
  55. return 0;
  56. }

程序的数据结果例如以下:

  1. guojun8@guojun8-desktop:~/test/valist$ ./main
  2. this is a testing, i = -1, u = 5, f = 0.200000

(3)      实现

以下介绍C语言可变长度參数的实现。事实上现与一个数据结构(va_list)和三个宏(va_start, va_end, va_arg)相关,从源代码中能够看到这些实现以下的来自linux内核源代码中的文件(include/acpi/platform/acenv.h)

  1. #ifndef _VALIST
  2. #define _VALIST
  3. typedef char *va_list;
  4. #endif        /* _VALIST */
  5.  
  6. /*
  7. * Storage alignment properties
  8. */
  9. #define  _AUPBND                (sizeof (acpi_native_int) - 1)
  10. #define  _ADNBND                (sizeof (acpi_native_int) - 1)
  11.  
  12. /*
  13. * Variable argument list macro definitions
  14. */
  15. #define _bnd(X, bnd)            (((sizeof (X)) + (bnd)) & (~(bnd)))
  16. #define va_arg(ap, T)           (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))
  17. #define va_end(ap)              (void) 0
  18. #define va_start(ap, A)         (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))

a)         va_list

从实现中能够看出va_list类型实际上就是一个指针。

b)        va_start

这个宏的作用是将T所指向的參数后面的内容放到ap中。当中_bnd (A,_AUPBND)是返回A的size并与系统的机器位数对齐。由于參数在栈中的地址一定是与系统的字长对齐的,当中acpi_native_int就表示机器字长。

c)         va_end

这个宏的作用就是返回0。

d)        va_arg

这个宏的作用是取得ap指向的当前的參数,并将ap指向參数列表中的下一个參数。

C语言可变长參数实现原理的更多相关文章

  1. C语言变长參数的认识以及宏实现

    1.认识 变长參数是C语言的特殊參数形式.比如例如以下函数声明: int printf(const char *format, ....); 如此的声明表明,printf函数除了第一个參数类型为con ...

  2. Java 变长參数Varargs

    Varargs (variable arguments)可变长參数是Java 1.5引入的特性. 方法的形參如print(String ... s),实參为随意数目的值. public class V ...

  3. java 变长參数使用原则

    1.java变长參数用...表示,如Print(String... args){  ... }; 2.假设一个调用既匹配一个固定參数方法.又匹配一个变长參数方法,则优先匹配固定參数的方法 3.假设一个 ...

  4. C++11 新特性之 变长參数模板

    template <typename ... ARGS> void fun(ARGS ... args) 首先明白几个概念 1,模板參数包(template parameter pack) ...

  5. C语言函数參数传递原理

    C语言中參数的传递方式一般存在两种方式:一种是通过栈的形式传递.还有一种是通过寄存器的方式传递的. 这次.我们仅仅是具体描写叙述一下第一种參数传递方式,第二种方式在这里不做具体介绍. 首先,我们看一下 ...

  6. linux kernel的cmdline參数解析原理分析

    利用工作之便,今天研究了kernel下cmdline參数解析过程.记录在此.与大家共享.转载请注明出处.谢谢. Kernel 版本:3.4.55 Kernel启动时会解析cmdline,然后依据这些參 ...

  7. [转]深度探索C语言函数可变长参数

    转自:http://www.cnblogs.com/chinazhangjie/archive/2012/08/18/2645475.html 一.基础部分 1.1 什么是可变长参数 可变长参数:顾名 ...

  8. object-c 不定參数的遍历和原理

    object-c接收随意类型的參数: /** * 接收String类型的多个參数 * @param firsParam 第一个參数 */ -(void)TestString:(NSString*)fi ...

  9. Effective JavaScript Item 22 使用arguments来创建接受可变參数列表的函数

    本系列作为Effective JavaScript的读书笔记. 在Item 21中,介绍了结合apply方法实现的可变參数列表函数average,它实际上仅仅声明了一个数组作为參数,可是利用apply ...

随机推荐

  1. Linux 基本命令-----常用操作分类

    Linux/Unix 命令格式: 命令名 [选项] [参数] 注:[]中的内容代表内容可以省略 例:$ ls $ ls -l #-l 是选项 开始符号: 文件名 或 文件夹名 .当前文件夹 ..上一级 ...

  2. 使用python3的typing模块提高代码健壮性

    前言:很多人在写完代码一段时间后回过头看代码,很可能忘记了自己写的函数需要传什么参数,返回什么类型的结果,就不得不去阅读代码的具体内容,降低了阅读的速度,加上Python本身就是一门弱类型的语言,这种 ...

  3. oracle精简客户端安装配置及常见问题

    有关Instant client 安装步骤 1.首先在官网下载两个安装包instant/sqlplus,对相关文件进行解压缩,存放本地路径 官网地址:http://www.oracle.com/tec ...

  4. Velocity(5)——#macro 指令

    1 #macro(formatIncreaseData $increase) 2 #if(${product.onlineStatusFlag} =='0') 3 -- 4 #elseif(!$inc ...

  5. VS2015如何连接mySQL数据库

    mySQL数据库           如题,今天给大家简单演示一下VS2015如何连接mySQL数据库.       首先呢,大家需要安装vs2015和mySQL这两个软件,我还安装了一个辅助软件SQ ...

  6. mysql存储过程分库分表

    -- 存储过程创建库  分为两条语句删除和创建DELIMITER $$USE myplan $$DROP PROCEDURE IF EXISTS createDBN $$CREATE PROCEDUR ...

  7. NodeJS 常用模块积累

    cluster&forever cluster & forever 虽然 nodejs 原生已经提供了 cluster 模块,大部分情况下可以满足我们的基本需求,但这两个模块 clus ...

  8. Redis全面介绍

    最近重新认识了一下Redis,借着这个机会,也整理一篇算是比较详尽和全面的文章吧.   缓存 缓存就是数据交换的缓冲区(称作Cache)——摘自百度百科.无论是在计算机硬件体系结构还是软件体系结构中, ...

  9. JavaScript DOM 编程艺术(1)---> JavaScript语法

    一.  JavaScript语法目录 语法 操作 条件语句 循环语句 函数 对象 二.  具体内容 2.1 语法 javaScript代码要通过HTML/XHTML文档才能执行.可以有两种方式完成这一 ...

  10. 记一次改造react脚手架的过程

    公司突然组织需要重新搭建一个基于node的论坛系统,前端采用react,上网找了一些脚手架,或多或少不能满足自己的需求,最终在基于YeoMan的react脚手架generator-react-webp ...