可變參數的使用-printf簡單實現

我們在寫一個程序的時候,經常用到一些函數,例如printf函數,在我們用的時候覺得並沒有什麼覺得他很簡單啊,我們使用的時候都沒有注意過,它其實有很多種調用方法。

例如:


其實這就是可變參數,我們看看printf函數的原型,是這樣的

 我們可以看到printf函數除了有一個固定的format參數以外,其他都是可變的,因此纔有了不同的調用方法



        既然這個函數這麼神奇,那麼在私底下他到底是怎麼實現的呢?接下來就這個問題,我們來一探究竟,同時希望可以幫助其他和我一樣對此不瞭解的人,也讓我加深對可變參數的理解,這裏我們就模擬實現一下printf函數。


  在可變參數的模擬實現中,我們會用到以下這幾個宏,這裏我在msdn中搜到他們


va在這裏是variable-argument(可變參數)的意思.這些宏定義在stdarg.h中,所以用到可變參數的程序應該包含這個頭文件,接下來我們就來寫一個簡單函數模擬實現一下printf函數

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
#include <stdlib.h>  
#include <stdarg.h>  

void display(int ret)   //打印整數
{
	if(ret>9)
	{
		display(ret/10);
	}
		putchar(ret%10+'0');
}
void print(char *format, ...)  //模擬實現printf函數
{
	va_list arg;    //char *
	va_start(arg, format);
	while(*format != '\0')
	{
		int i = 0;
		if(*format == 's')    //出現s,就打印一個字符串
		{
			char* ret = NULL;
			ret = va_arg(arg, char*);
			while(*ret != '\0')
			{
			putchar(*ret);
			ret++;
			}
		}
		else if(*format == 'c')    //出現c,就打印一個字符
			putchar(va_arg(arg, char));
		else if(*format == 'd')    //出現d,就打印整型,這裏沒有打印負數
		{
			int ret = 0;
			ret = va_arg(arg, int);
			display(ret);
		}
		else                   //其他照常打印
			putchar(*format);
		    	format++;
	}
	va_end(arg);
}
int main()
{
	print("s ccc d%.\n","hello",'b','i','t',100);
	return 0;
}


接下來我們用畫圖的方式來分析

首先爲MAIN函數開闢一塊空間



然後在MAIN函數中調用printf函數,調用的時候有會進行傳參


然後通過內存進行訪問,模擬實現了printf函數

從這個函數的實現可以看到,我們使用可變參數應該有以下步驟: 


  1)首先在函數裏定義一個va_list型的變量(依照個人情況),這裏是arg,這個變量是指向參數的指針.
  2)然後用va_start宏初始化變量arg。
  3)然後用va_arg返回可變的參數,並賦值,va_arg的第二個參數是你要返回的參數的類型,這裏是char型(返回字符串“hello”,字符‘b’,‘i’,‘t’),int型(返回100). 對應的返回值可以在程序中找到。
  4)最後用va_end宏結束可變參數的獲取。


接下我們看看可變參數在編譯器中的處理情況

typedef System::ArgIterator va_list;
#else
typedef char *  va_list;
#endif /* _M_CEE_PURE */
#define _VA_LIST_DEFINED
#endif

#ifdef  __cplusplus
#define _ADDRESSOF(v)   ( &reinterpret_cast<const char &>(v) )
#else
#define _ADDRESSOF(v)   ( &(v) )
#endif

#if     defined(_M_IA64) && !defined(_M_CEE_PURE)
#define _VA_ALIGN       8
#define _SLOTSIZEOF(t)   ( (sizeof(t) + _VA_ALIGN - 1) & ~(_VA_ALIGN - 1) )

#define _VA_STRUCT_ALIGN  16 

#define _ALIGNOF(ap) ((((ap)+_VA_STRUCT_ALIGN - 1) & ~(_VA_STRUCT_ALIGN -1)) \
        - (ap))
#define _APALIGN(t,ap)  (__alignof(t) > 8 ? _ALIGNOF((uintptr_t) ap) : 0)

#else
#define _SLOTSIZEOF(t)   (sizeof(t))
#define _APALIGN(t,ap)  (__alignof(t))
#endif

#if     defined(_M_CEE)

extern void __cdecl __va_start(va_list*, ...);
extern void * __cdecl __va_arg(va_list*, ...);
extern void __cdecl __va_end(va_list*);

#define _crt_va_start(ap,v)  ( __va_start(&ap, _ADDRESSOF(v), _SLOTSIZEOF(v), \
                                __alignof(v), _ADDRESSOF(v)) )
#define _crt_va_arg(ap,t)    ( *(t *)__va_arg(&ap, _SLOTSIZEOF(t), \
                                _APALIGN(t,ap), (t *)0) )
#define _crt_va_end(ap)      ( __va_end(&ap) )

#elif   defined(_M_IX86)

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

#define _crt_va_start(ap,v)  ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
#define _crt_va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define _crt_va_end(ap)      ( ap = (va_list)0 )

#elif defined(_M_IA64)

va_arg()取得類型的可變參數值

#define va_arg _crt_va_arg

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



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章