『OOC』ノート(3)——C言語が長くなるパラメータva_listの使い方

4803 ワード

『OOC』ノート(3)——C言語が長くなるパラメータva_listの使い方
C言語で有名なprintf関数は,許容できるパラメータ数が固定されていない,これが長くなるパラメータである.C#にはparamsというキーワードもあり、長くなるパラメータを実現します.
1 printf("Hello Mozart!");
2 printf("Hello %s!", "Mozart");
3 printf("%d: Hello %s!", 77, "Mozart");

Cで1つの長くなるパラメータの関数を受け入れることができることを実現します
例として次のようになります.
 1 #include <stdarg.h>
 2 
 3 int add(const char * testString, int x, ...) 
 4 {
 5     printf("%s
", testString); 6 va_list list; 7 va_start(list, x); 8 int result = 0; 9 10 for(;;) 11 { 12 int p = va_arg(list, int); 13 if(p == 0) 14 break; 15 result += p; 16 } 17 va_end(list); // cleanup , set 'lsit' to NULL 18 return result; 19 } 20 21 /* error: ISO C requires a named argument before '...' 22 void add2(...) 23 { 24 } 25 */ 26 27 int main() 28 { 29 int result = add("test case 1: ", 1, 2, 3, 4, 5, 0); 30 printf("%d
", result); 31 system("PAUSE"); 32 return 0; 33 } 34 35 /* 36 This program print as follows: 37 test case 1: 38 15 39 */

変長パラメータを使用する関数を記述するには、次の手順に従います.

  • まずstdargを参照する.h.

  • 次に、関数宣言で「...」を使用します.この関数が長くなるパラメータを使用できることを示します.

  • 注意する前には少なくとも普通のパラメータが必要です.(標準Cではないかもしれませんが、保守的なほうがいいです)

  • では、これらの数、タイプが不確定なパラメータをどのように使用しますか?

  • va_Listタイプのlist変数は「...」を巡回できます.に表示されます.
    vaでstart()を使用してlist変数を初期化します.va_start()は「...」に隣接する必要がありますの左にあるパラメータ名です.(例のx)
    va_arg()は、次のパラメータ値を取得するために使用されます.このパラメータ値のタイプは、符号化時に決定する必要があります.
    va_end()はlistのループを終了するために使用されます.その後vaを再利用できますstart()、va_arg()、va_end()は、各可変パラメータ値を順次取得する.
     
    注意事項
    addのこの例では、可変パラメータの処理が完了したことを知るには、最後のパラメータは0でなければなりません.他に方法はない.つまり、外力を借りずに伝わる可変パラメータがいくつあるかを知ることはできません.
    printf("%d,%s,%c",1,"11",'1');関数ではprintfがフォーマットパラメータ「%d,%s,%c」を解析し、3つのフォーマット出力記号が表示されるので、3つの可変パラメータが入力されたと考えられます.もしあなたが多く伝わったり少なくなったりしたら、プログラムが間違っている可能性があります.コンパイラはこのエラーを検出できません.
    list変数はパラメータとして他の関数に渡すことができます.(例えばvprintf(「xxx」,list);)
    可変パラメータを渡すとintまたはlongとして整数が渡され、float型はdoubleとして渡されます.
    va_arg()の2番目のパラメータ(例のint)は、あまり複雑ではないはずです.(この話はあいまいだ)
    要するに,C言語における変長パラメータの使用は良いプログラミング実践ではない.なるべく避けられる.
     
    原理は何ですか.
    typedef char* va_list
    #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)

    va_リストはstdioです.hで定義されたタイプ.va_start、va_arg、va_endは3つのマクロ定義です.
    va_startはlistに((vのアドレス)+(vの長さ))を付与する.関数呼び出し時のパラメータのメモリレイアウトに基づいてlistは最初の可変パラメータを指します.(例2)
    毎回va_を呼び出すargは、現在のパラメータ値を取得し、apポインタを次のパラメータに指します.
    呼び出しva_endはapを0にリセットします.
    したがって、この可変パラメータの原理は、関数スタックのパラメータ上を移動して可変パラメータを順次取得する反復器である.