c语言可变参数 printf,【C语言】浅谈可变参数与printf函数(示例代码)

一.何谓可变参数

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

这是使用过C语言的人所再熟悉不过的printf函数原型,它的参数中就有固定参数format和可变参数(用”…”表示).

而我们又可以用各种方式来调用printf,如:

printf( "%d ",value);

printf( "%s ",str);

printf( "the number is %d ,string is:%s ", value, str);

二.实现原理

C语言用宏来处理这些可变参数。

这些宏看起来很复杂,其实原理挺简单:就是根据参数入栈的特点从最靠近第一个可变参数的固定参数开始,依次获取每个可变参 数的地址.下面我们来分析这些宏.

在VC中的stdarg.h头文件中,针对不同平台有不同的宏定义,我们选取X86平台下的宏定义:

typedef char *va_list;

#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

#define va_start(ap,v)( ap = (va_list)&v + _INTSIZEOF(v) )

#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

#define va_end(ap) ( ap = (va_list)0 )

可以用下图来表示:

在VC等绝大多数C编译器中,默认情况下,参数进栈的顺序是由右向左的,因此,参数进栈以后的内存模型如下图所示:最后一个固定参数的地址位于第一个可变参数之下,并且是连续存储的。

|——————————————————————————|

|最后一个可变参数 | -> 高内存地址处

|——————————————————————————|

...................

|——————————————————————————|

|第N个可变参数 | ->

va_arg(arg_ptr,int)后arg_ptr所指的地方,

| | 即第N个可变参数的地址。

|——————————————— |

………………………….

|——————————————————————————|

|第一个可变参数 | ->

va_start(arg_ptr,start)后arg_ptr所指的地方

| | 即第一个可变参数的地址

|——————————————— |

|———————————————————————— ——|

| |

|最后一个固定参数 | -> start的起始地址

|—————————————— —| .................

|—————————————————————————— |

| |

|——————————————— |-> 低内存地址处

三.printf研究

下面是一个简单的printf函数的实现,参考了中的156页的例子,读者可以结合书上的代码与本文参照。

#include

#include

//一个简单的类似于printf的实现,//参数必须都是int 类型

void myprintf(char* fmt, ...){

char* pArg=NULL; //等价于原来的va_list

char c;

pArg = (char*)&fmt; //注意不要写成p = fmt !!因为这里要对参数取址,而不是取值

pArg += sizeof(fmt); //等价于原来的va_start

do{

c =*fmt;

if (c != ‘%‘){

putchar(c); //照原样输出字符

}else{

//按格式字符输出数据

switch(*(++fmt)){

case ‘d‘:

printf( "%d",*((int*)pArg));

break;

case ‘x‘:

printf( "%#x",*((int*)pArg));

break;

default:

break;

}

pArg += sizeof(int); //等价于原来的va_arg

}

++fmt;

}while (*fmt != ‘‘);

pArg = NULL; //等价于va_end

return;

}

int main(){

int i = 1234;

int j = 5678;

myprintf( "the first test:i=%d

",i);

myprintf( "the secend test:i=%d; %x;j=%d; ",i,0xabcd,j);

system( "pause ");

return 0;

}

输出:

the first test:i=1234

the secend test:i=1234; 0xabcd;j=5678;

四.应用

求最大值:

#include //不定数目参数需要的宏

#include

int max(int n,int num, ...){

int m = num;

int i = 0;

va_list x;//说明变量x

va_start(x,num);//x被初始化为指向num后的第一个参数

for(i=1; i

//将变量x所指向的int类型的值赋给y,同时使x指向下一个参数

int y = va_arg(x,int);

if(y>m){

m=y;

}

}

va_end(x);//清除变量x

return m;

}

int main(){

int ret1 = max(3,5,56,55);

int ret2 = max(6,0,4,32,45,533,6565);

printf( "%d,%d ",ret1,ret2);

return 0;

}

输出:

56,6565

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值