可变参数列表源码的剖析

来源:互联网 发布:男人影视软件下载 编辑:程序博客网 时间:2024/05/21 14:16

C语言中的可变参数

1、C语言中的可变参数列表:C语言中有时候会遇到一些参数个数是可变的函数,像scanf,printf等,这些函数内部的参数列表都是可变的。通过将函数实现为可变参数的形式,使得函数可以接受1个以上的任意多个参数。(不固定)

如函数原型:scanf(const char * format,…);
解释:除了format以外,后面跟着的参数 个数,类型是可变的,用…作为一种占位符号。“…”称为可变参数列表,可以用来接受个数和类型不确定的参数。

再例如printf()是一个支持可变参数的函数,可以有多个参数。函数原型:int printf(const char *format,...)   省略号代表参数的个数是不固定的。

(1)可变参数列表在C语言中使用这样的三个宏(va_start,va_arg,va_end)和一个类型(va_list)来进行实现的,他们是定义在stdarg.h头文件中的。

1)va_start原型

void va_start(va_list ap,paramN);

  参数:va_list:存储参数的类型信息
       ap:可变参数列表地址
       paramN:确定的参数
    2)va_arg原型
       void va_arg(va_list ap,type);返回下一个参数的值
    3)va_end原型
       void va_end(va_list ap,type);关闭初始化的列表

使用方式:
   1) 使用va_list初始化可变参数列表
   2) 使用va_arg逐个获取参数值
   3)用va_end将可变参数列表清空

  可变参数列表的限制条件:至少有一个参数

例子: 
模拟prinf函数的实现:

#define _CRT_SECURE_NO_WARNINGS#include <stdio.h>#include <stdarg.h>#include <string.h>#include <stdlib.h>void reverse(char * buf)          //将按顺序打印出来的字符串翻转,被myitoa函数调用。{int len = strlen(buf);int i = 0, j = len-1;while (i < j) {char c = buf[i];buf[i] = buf[j];buf[j] = c;i++;j--;}}char *myitoa(int n)         //将int型的数字转化为字符串,100(int)->"001",通过reverse函数"001"->"100".{static char buf[100];int i = 0;memset(buf, 0x00, sizeof(buf));        //将buf数组清零do {buf[i++] = (n % 10)+'0';n /= 10;} while (n != 0);        reberse(buf);return buf;}void myputs(const char *buf)    //打印被myitoa转换好的字符串。{int i;for (i = 0; buf[i] != 0; i++)putchar(buf[i]);}void myprint(char * p,...)       //模拟printf函数{va_list str;char* pa;va_start(str, p);int i = 0;for (i = 0; p[i] != 0; i++) {
if (p[i] == '%') {i++;switch (p[i]){case 'd':{int data = va_arg(str, int);myputs(myitoa(data));}break;case 'c':{int c = va_arg(str, char);putchar(c);}break;case 's':{char *s = va_arg(str, char *);for (; *s; s++)putchar(*s);}break;default:break;}}else {putchar(p[i]);}}va_end(str);}int main(){myprint("s=%s %s word! % c %c %c = %d,\n", "s ccc d.\n","hello" ,'b','i','t',100);system("pause");return 0;}

 头文件<stdarg.h>提供了

遍历未知数目和类型的函数参数列表的功能。



2、实现可变参数的要点就是想办法取得每个参数的地址,取得地址的办法由以下几个因素决定:

①函数栈的生长方向

②参数的入栈顺序

③CPU的对齐方式

④内存地址的表达方式



3、可变参数的限制 :

  • 可变参数必须从头到尾逐个访问。如果你在访问了几个可变参数之后想半途终止,这是可以的,但是,如果你想一开始就访问参数列表中间的参数,那是不行的。
  • 参数列表中至少有一个命名参数。如果连一个命名参数都没有,就无法使用va_start
  • 这些宏是无法直接判断实际存在参数的数量。
  • 这些宏无法判断每个参数的是类型。
  • 如果在va_arg中指定了错误的类型,那么其后果是不可预测的。


原创粉丝点击