zoukankan      html  css  js  c++  java
  • 【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 <stdio.h>
    #include <stdlib.h>
    //一个简单的类似于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 <stdarg.h>//不定数目参数需要的宏
    #include <stdio.h>
    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<n; 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
    
      相关推荐
    1. 【C语言】模拟实现printf函数(可变参数)
    2. printf函数功能、原型、用法及实例

    (本文来源于互联网,若有侵权,请联系博主)

  • 相关阅读:
    单位表示
    linux 文件权限
    php中高级基础知识点
    CodeIgniter配置之SESSION
    提高PHP开发质量的36个方法(精品)
    数据库优化举例详解
    ajax 跨域解决 网上资料
    static(静态)关键字
    假如java类里的成员变量是自身的对象
    Java 静态代码块&构造代码块&局部代码块
  • 原文地址:https://www.cnblogs.com/qq329914874/p/5995044.html
Copyright © 2011-2022 走看看