C中的可變參數研究

來源:互聯網
上載者:User
C中的可變參數研究  
   
  一.   何謂可變參數  
  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;    
  /*把va_list被定義成char*,這是因為在我們目前所用的PC機上,字元指標類型可以用來儲存記憶體單元地址。而在有的機器上va_list是被定義成void*的*/  
  #define   _INTSIZEOF(n)   (   (sizeof(n)   +   sizeof(int)   -   1)   &   ~(sizeof(int)   -   1)   )  
  /*_INTSIZEOF(n)宏是為了考慮那些記憶體位址需要對齊的系統,從宏的名字來應該是跟sizeof(int)對齊。一般的sizeof(int)=4,也就是參數在記憶體中的地址都為4的倍數。比如,如果sizeof(n)在1-4之間,那麼_INTSIZEOF(n)=4;如果sizeof(n)在5-8之間,那麼_INTSIZEOF(n)=8。*/  
  #define   va_start(ap,v)(   ap   =   (va_list)&v   +   _INTSIZEOF(v)   )  
  /*va_start的定義為   &v+_INTSIZEOF(v)   ,這裡&v是最後一個固定參數的起始地址,再加上其實際佔用大小後,就得到了第一個可變參數的起始記憶體位址。所以我們運行va_start(ap,   v)以後,ap指向第一個可變參數在的記憶體位址*/  
  #define   va_arg(ap,t)   (   *(t   *)((ap   +=   _INTSIZEOF(t))   -   _INTSIZEOF(t))   )  
  /*這個宏做了兩個事情,  
  ①用使用者輸入的類型名對參數地址進行強制類型轉換,得到使用者所需要的值  
  ②計算出本參數的實際大小,將指標調到本參數的結尾,也就是下一個參數的首地址,以便後續處理。*/  
    #define   va_end(ap)   (   ap   =   (va_list)0   )    
  /*x86平台定義為ap=(char*)0;使ap不再   指向堆棧,而是跟NULL一樣.有些直接定義為((void*)0),這樣編譯器不會為va_end產生代碼,例如gcc在linux的x86平台就是這樣定義的.   在這裡大家要注意一個問題:由於參數的地址用於va_start宏,所以參數不能聲明為寄存器變數或作為函數或數群組類型.   */  
   
  以下再用圖來表示:  
   
  在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"  
  void   myprintf(char*   fmt,   ...)   //一個簡單的類似於printf的實現,//參數必須都是int   類型  
  {    
  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   !=   '/0');    
  pArg   =   NULL;   //等價於va_end  
  return;    
  }  
  int   main(int   argc,   char*   argv[])  
  {  
  int   i   =   1234;  
  int   j   =   5678;  
   
  myprintf("the   first   test:i=%d",i,j);    
  myprintf("the   secend   test:i=%d;   %x;j=%d;",i,0xabcd,j);    
  system("pause");  
  return   0;  
  }  
  在intel+win2k+vc6的機器執行結果如下:  
  the   first   test:i=1234  
  the   secend   test:i=1234;   0xabcd;j=5678;  
   
  四.應用  
  求最大值:  
  #include   //不定數目參數需要的宏  
  int   max(int   n,int   num,...)  
  {  
  va_list   x;//說明變數x  
  va_start(x,num);//x被初始化為指向num後的第一個參數  
  int   m=num;  
  for(int   i=1;i   {  
  //將變數x所指向的int類型的值賦給y,同時使x指向下一個參數  
  int   y=va_arg(x,int);  
  if(y>m)m=y;  
  }  
  va_end(x);//清除變數x  
  return   m;  
  }  
  main()  
  {  
  printf("%d,%d",max(3,5,56),max(6,0,4,32,45,533));  
  }   
 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.