C语言可变长參数实现原理


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


 (1)      C语言可变參数

我们能够从C语言的printf得出可变參数的作用。printf函数的原型例如以下:
int printf ( const char * format, ... );
通过使用可变个数參数,就是传入的參数个数是可变的,如printf须要依据format实參传入多个实參。

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

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

/*
 * Author: guojun07
 */

#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>

void myprintf(char *format, ...) {
  va_list ap;
  int pos = 0;
  int int_val = 0;
  float f_val;
  char buf[64];
  memset(buf, 0, 64);
  // 得到全部的參数放到下一个list中ap中
  va_start(ap, format);
  while (format[pos] != '') {
    // 推断'%'。表示要得到下一个參数
    if (format[pos] == '%') {
      pos ++;
      switch(format[pos]) {
        case 'd':
        case 'u':
          // 得到ap中的下一个參数
          int_val = va_arg(ap, int);
          sprintf(buf, "%d", int_val);
	  // 将数据写到标准输出
          write(STDOUT_FILENO, buf, strlen(buf));
          memset(buf, 0, 64);
          pos ++;          
	  break;
        case 'f':
          // 得到ap中的下一个參数
          f_val = (float)va_arg(ap, double);
          sprintf(buf, "%f", f_val);
	  // 将数据写到标准输出
          write(STDOUT_FILENO, buf, strlen(buf));
          memset(buf, 0, 64);
          pos ++;
          break;
        default:
          break;
      }
    } else {
      write(STDOUT_FILENO, &(format[pos]), 1);
      pos ++;
    }
  }
}

int main(void){
  myprintf("this is a testing, i = %d, u = %u, f = %f
", -1, 5, 0.2);
  return 0;
}
程序的数据结果例如以下:
guojun8@guojun8-desktop:~/test/valist$ ./main
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)
#ifndef _VALIST
#define _VALIST
typedef char *va_list;
#endif        /* _VALIST */
 
/*
* Storage alignment properties
*/
#define  _AUPBND                (sizeof (acpi_native_int) - 1)
#define  _ADNBND                (sizeof (acpi_native_int) - 1)
 
/*
* Variable argument list macro definitions
*/
#define _bnd(X, bnd)            (((sizeof (X)) + (bnd)) & (~(bnd)))
#define va_arg(ap, T)           (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))
#define va_end(ap)              (void) 0
#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指向參数列表中的下一个參数。 

原文地址:https://www.cnblogs.com/zsychanpin/p/6932691.html