从printf的实现原理来谈对C/C++中可变参数函数的实现理解

 

  printf函数原型:int _cdecl printf(const char*format, …);


首先传递给函数的参数中要有函数用以了解到参数个数的信息,比如printf开始的format字符串,通过%来标识变量,如printf("hello my rank is %d in %d",i,j);printf搜寻第一个参数format指向的字符串"hello my rank is %d in %d"中的%标识,来确定紧跟format之后是两个int变量,再格式化字符串并输出。

 

细节问题及函数代码说明如下:

1._cdecl是C和C++程序的缺省调用方式,这种方式下参数从右向左入栈(最后压format参数),调用者负责清理堆栈。这种方式适合变参函数的实现,函数调用者知道压入到堆栈多少数据,所以调用完函数后可以相应清理多少数据。

2.如果想实现自己的变参函数,可用到如下的宏定义(这是X86平台的宏定义):

 typedef char *  va_list;
  #define_INTSIZEOF(n)  
           ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
  #define va_start(va_list  ap, format) //得到第一个参数的首地址
            ( ap = (va_list)& format+ _INTSIZEOF(format) )

  #define va_arg(va_list  ap,type)   //将参数转换成需要的类型,并使ap指向下一个参数
            ( *(type*)((ap += _INTSIZEOF(type)) -_INTSIZEOF(type)) ) //注意括号

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


3.下面以这几个宏来模拟部分实现printf函数(完全可以不用到宏,只是标准库为了定义变参函数的方便)

#include 
	#include 
	void myprintf(const char *format, ...)
	{
        	va_list ap;
        	char ch;
       		 va_start(ap, format);
       		 while(ch = *format++)
        	{
                		switch(c)
                		{
                       			 case 'c':
                                		{
                                        		char ch1 = va_arg(ap, char);
                                       			putchar(ch1);
                                       			break;
                                		}

 					case 's':
             					{
                      					char *p = va_arg(ap, char *);
                      					fputs(p,stdout);
                      					break;
            					  }
              				default:
                       				putchar(ch);
       			 }//switch
  	 	}//while 
        	va_end(ap);
	}//myprintf



4.栈中参数分布以及宏使用后的指针变化说明如下:



-------------------以上代码图片部分参考自网络白雪-------------------



总结:

对可变参数函数的编写用到上面提到的一些宏就行了,当然也需要是_cdecl的函数调用方式(这是默认的)。通过探究这一主题,也加深了对函数调用方式,函数堆栈参数分布的了解。

你可能感兴趣的:(C/C++)