C/C++における可変パラメータの使い方の詳細解析

5889 ワード

可変パラメータは、パラメータの個数が変化してもよく、多かれ少なかれ、パラメータのタイプが変化してもよいことを示し、intであってもよく、doubleはchar*、クラス、構造体などであってもよい.可変パラメータはprintf(),sprintf()などの関数を実現する鍵であり,任意の数のデータを可変パラメータで和を求め,平均値を求めることが便利である(そうでなければ配列や各書き込みでリロードする).C#には専用のキーワードparameがありますが、C,C++には似たような構文はありませんが、幸いなことにこの方面の処理関数を提供しており、本稿ではこれらの関数の使い方に重点を置いて説明します.第1ステップの可変パラメータ表現は3つの点...で表され、printf()関数とscanf()関数の宣言を表示します:int printf(const char*,...);int scanf(const char *, ...);
この3つのポイントがマクロで使用されるのは、変数マクロ(Variadic Macros)で、デフォルト名は__です.VA_ARGS__.例:#define WriteLine(...){ printf(__VA_ARGS__); putchar('');}さらにWriteLine("MoreWindows");
考慮するとprintf()の戻り値は出力を表すバイト数である.上のマクロを、#define WriteLine(...)に変更します.printf(__VA_ARGS__) + (putchar('') != EOF ? 1: 0);これにより、WriteLineマクロの戻り値が得られ、最後の''を含む出力のバイト数が返されます.以下の例に示すiおよびjは、いずれも12を出力する.
 
  
       int i = WriteLine("MoreWindows");
       WriteLine("%d", i);
       int j = printf("%s
", "MoreWindows");
       WriteLine("%d", j);

第2ステップva_の処理方法Listタイプ関数の内部は可変パラメータに対してva_を使用します.Listとそれに関連する3つのマクロを処理することは,パラメータを実現する鍵である.
でva_を見つけることができますリストの定義:
typedef char *  va_list;
さらに、それと密接な関係にある3つのマクロについて説明します.va_start(),va_end()とva_arg().
同様に、この3つのマクロの定義を見つけることができます:#define va_start(ap,v)  ( ap = (va_list)&v + _INTSIZEOF(v) )#define va_end(ap)      ( ap = (va_list)0 )#define va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZOF(t))に使用される_INTSIZOFマクロの定義は、#define_INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
この4つのマクロを分析します:va_end(ap)これが一番簡単なのは、ポインタをNULLにすることです.va_start(ap,v)におけるap=(va_list)&v+INTSIZOF(v)はまずvの住所を取って、それから_を加えますINTSIZEOF(v)._INTSIZOF(v)はちょっと複雑です.((sizeof(n)+sizeof(int)-1)&~(sizeof(int)-1))はすべてビット操作で、少し面倒に見えますが、実はそうではありません.とても簡単で、sizeof(int)に整列します.たとえばsizeof(int)は4,1,2,3,4で4,5,6,7,8で8をとる.xからnへの整列用C言語の算術表現は((x+n-1)/n)*nであり、nが2のべき乗である場合、最後の2ステップ演算をビット操作に置き換えることができる――最低n-1のバイナリビットを0にすればよい.va_arg(ap,t)とは、apからタイプtのデータを取り出し、ポインタを後方に移動することである.例えばva_arg(ap,int)は、intデータを取り出し、ポインタを4バイト移動することを示す.したがって、関数ではva_を先に使用します.start()は、変パラメータの開始アドレスを取得し、va_を使用します.arg()は1つずつ値を取り、最後にva_を使います.end()の最後に可変パラメータを解析できます.第3ステップvfprintf()関数とvsprintf()関数vfprintf()という関数は重要で、名前だけでよく使われるprintf()関数と大きく関連していることがわかります.複数のリロード・バージョンがあります.ここでは、関数のプロトタイプについて説明します.
 
  
int vfprintf(

   FILE *stream,

   const char *format,

   va_list argptr

);


最初のパラメータはFILEポインタです.FILE構造のC言語での読み書きファイルは欠かせない.スクリーンに転送stdoutを出力するには.
2番目のパラメータは、出力のフォーマットを指定します.
3番目のパラメータはva_listタイプ、これは珍しいですが、実はchar*が可変パラメータを表す開始アドレスです.
戻り値:出力が正常に返されたバイト数(最後の'0'を除く)、エラーは-1を返します.
vsprintf()は、上記の関数と同様に、関数のプロトタイプのみがリストされます.
 
  
int vsprintf(

   char *buffer,

   const char *format,

   va_list argptr

);


もう一つint_vscprintf(const char *format, va_list argptr );vsprintf()関数のbuffer文字列がどれだけのバイトを必要とするかを計算するために使用できます.
コード例は以下に,自己実現printf()関数(注1)とWriteLine()関数を示す.
 
  
 int Printf(char *pszFormat, ...) 
{
       va_list   pArgList;

       va_start(pArgList, pszFormat);
       int nByteWrite = vfprintf(stdout, pszFormat, pArgList);
       va_end(pArgList);

       return nByteWrite;
}

int WriteLine(char *pszFormat, ...)
{
       va_list   pArgList;

       va_start(pArgList, pszFormat);
       int nByteWrite = vfprintf(stdout, pszFormat, pArgList);
       if (nByteWrite != -1)
              putchar('
'); // 2
       va_end(pArgList);

       return (nByteWrite == -1 ? -1 : nByteWrite + 1);
}

呼び出しはprintf()関数と同じです.
可変パラメータで和を求めると、残念ながらC,C++では入力された可変パラメータの個数を特定できない(printf()では'%'個数をスキャンすることでパラメータの個数を確実にする)ので、個数を指定するか、パラメータの最後に哨兵数値を設定するか:
歩哨の数値を設定します.
 
  
const int GUARDNUMBER = 0; //
// , printf() '%' ,
int MySum(int i, ...)
{
       int sum = i;
       va_list argptr;

       va_start(argptr, i);
       while ((i = va_arg(argptr, int)) != GUARDNUMBER)
              sum += i;
       va_end(argptr);

       return sum;
}

printf("%d",MySum(1,3,5,7,9,0);
ただし、0:printf("%d",MySum(0);//error
数を指定:
 
  
int MySum(int nCount, ...)
{
       if (nCount <= 0)
              return 0;

       int sum = 0;
       va_list argptr;

       va_start(argptr, nCount);
       for (int i = 0; i < nCount; i++)
              sum += va_arg(argptr, int);
       va_end(argptr);

       return sum;
}

呼び出し時の最初のパラメータは、次のような後のパラメータの数を表します.
       printf("%d", MySum(5, 1, 3, 5, 7, 9));
       printf("%d", MySum(0));
コードに使用されるヘッダファイル:#include#include可変パラメータの使用方法は上記の数だけではありませんが、C,C++で可変パラメータを使用する場合は注意してください.printf()などの関数を使用する場合に入力されるパラメータの個数は、前のフォーマット文字列の'%'記号の個数より少なくないようにしてください.そうしないとアクセス限界が発生し、運が悪いとプログラムがクラッシュします.可変パラメータの原形理は関数を呼び出す際のパラメータのスタック問題に関し,これは次回から専門的な検討を行う.注1.インターネット上ではvfprintf()が自分でパラメータを解析することなくprintf()を実現することはめったにありませんが、printf()に近い機能を実現することはめったにありません(実際にprintf()を完全に熟知している人はもう多くありません.信じなければ、「Cトラップと欠陥」を見てprintf()のあまりよく使われていないパラメータを理解し、Microsoft Visual StudioVC 98CRTSRCでOUTPUTを見ることができます.C対printf()の実装).注意2.単一文字putchar(ch)を出力するとprintf("%c",ch)よりも効率が高くなります.文字列が長くない場合、printf("%s",szStr)を呼び出すよりもputchar()を複数回呼び出す方がよい.の効率が高い.関数が大量に呼び出される場合に非常に顕著です.