可変パラメータ:va_start,va_arg,va_end


va_start,va_arg,va_endはstdargです.hでマクロとして定義され、
1)ハードウェアプラットフォームの違い2)コンパイラの違いにより定義されるマクロも異なる.
面はVC++でstdarg.h里x 86プラットフォームのマクロ定義の抜粋は以下の通りである(''号は折り返し行を表す).
typedef char * va_list; 
#define _INTSIZEOF(n)\
((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) ) 
#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 ) 
 
もう1つの実装:
typedef char *    va_list;//va_listはchar*、すなわち文字ポインタに等しい.define va_start _crt_va_start//次の代替に注意してください.define va_arg _crt_va_arg #define va_end _crt_va_end #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 )  va_list argptr; 定義_INTSIZOF(n)は、主にメモリの整列が必要なシステムのためである.C言語の手紙
数は右から左へスタックに押し込むものであり、図(1)は関数のパラメータのスタックにおける分布位置である.わたし
我々はvaを見たlistはchar*として定義、いくつかのプラットフォームやオペレーティングシステムはvoid*として定義.また
va_を見てstartの定義、定義は&v+INTSIZOF(v),&vはスタックに固定パラメータ
アドレスなのでva_を実行しますstart(ap,v)以降、apはスタック内の最初の可変パラメータを指す
図のようにスタックのアドレス:
ハイアドレス|-----------------------------------------------------------|
|関数返却先|
|-----------------------------|
|....... |
|-----------------------------|
|n番目のパラメータ(最初の可変パラメータ)|
|-----------------------------|<--va_start後ap指向
|n-1番目のパラメータ(最後の固定パラメータ)|
低アドレス|-------------------------------------------------<--&v
図(1)
そして、va_Arg()はタイプtの可変パラメータ値を取得し、以上の例はint型を例に、私は
vaを見てみましょうArgはint型の戻り値をとる:
j= ( *(int*)((ap += _INTSIZEOF(int))-_INTSIZEOF(int)) );
まずap+=sizeof(int)は、次のパラメータのアドレスを指す.そして戻る
ap-sizeof(int)のint*ポインタ、これはまさに最初の可変パラメータのスタック内のアドレスです
(図2).そして*で取得するこのアドレスの内容(パラメータ値)をjに与える.
ハイアドレス|-----------------------------------------------------------|
|関数返却先|
|-----------------------------|
|....... |
|-----------------------------|<--va_Arg後ap指向
|n番目のパラメータ(最初の可変パラメータ)|
|-----------------------------|<--va_start後ap指向
|n-1番目のパラメータ(最後の固定パラメータ)|
低アドレス|-------------------------------------------------<--&v
図(2)
最後に言いたいのはva_endマクロの意味で、x 86プラットフォームはap=(char*)0と定義されています.apを無効にする
スタックを指すのではなく、NULLと同じです.コンパイラが
va_になりますend生成コード、例えばgccはlinuxのx 86プラットフォームでこのように定義.
ここでは、パラメータのアドレスがva_に使用されているため、注意してください.startマクロ
パラメータではレジスタ変数として宣言できないか、関数や配列タイプとして宣言する.
va_についてstart, va_arg, va_endの説明はこれです.注意しなければなりません.
異なるオペレーティングシステムとハードウェアプラットフォームの定義は少し異なるが、原理は似ている. 
(三)可変パラメータのプログラミングにおいて注意すべき問題
なぜならva_start, va_arg, va_endなどはマクロと定義されているので愚かに見えますが、
可変パラメータのタイプと個数は、この関数でプログラムコードによって完全に制御され、スマートではありません.
異なるパラメータの個数とタイプを識別する.
ではprintfではスマート識別パラメータが実現されているのではないでしょうか.それは関数が
printfは、固定パラメータformat文字列からパラメータのタイプを解析し、va_を呼び出すarg
変数パラメータを取得する.つまり、知能認識可変パラメータを実現するには
自分のプログラムの中で判断して実現したことがある.
もう一つの問題は,コンパイラが可変パラメータの関数のプロトタイプ検査が不十分であるためである.
格、プログラミングの間違いに不利です.simple_va_fun()を次のように変更します.
void simple_va_fun(int i, ...)
{
va_list arg_ptr;
char *s=NULL; 
va_start(arg_ptr, i);
s=va_arg(arg_ptr, char*);
va_end(arg_ptr);
printf("%d %s", i, s);
return;
}
可変パラメータはchar*型で、2つのパラメータで関数を呼び出すのを忘れた場合に表示されます.
Core dump(Unix)またはページ不正エラー(windowプラットフォーム).でも出ないかもしれない
間違いですが、間違いは発見しにくいので、高品質のプログラムを書くのに不利です.
以下、vaシリーズマクロの互換性について述べる.
System V Unix把va_startは、1つのパラメータのみのマクロとして定義されます.
va_start(va_list arg_ptr);
ANSI Cは次のように定義されます.
va_start(va_list arg_ptr, prev_param);
システムVの定義を使うならvarargを使うべきだ.hヘッダファイルで定義された
マクロ、ANSI CのマクロはシステムVのマクロと互換性がありません.私たちは一般的にANSI Cを使っています.
ANSI Cの定義で十分で、プログラムの移植にも便利です. 
va_List-----このようなリソースを申請します
va_start---リソースを初期化
va_Arg----リソース内の変数値を得る
va_end----リソースの解放
------------------------------------------------------------------------------------------------------------------------------------

va_arg


構文:
#include <stdarg.h>
type va_arg( va_list argptr, type );
void va_end( va_list argptr );
void va_start( va_list argptr, last_parm );

機能:
マクロ#マクロ#
va_arg()
関数に可変長を渡すパラメータのリスト.
  • まずva_を呼び出す必要がありますstart()有効なパラメータリストva_を渡すlistと関数が強制する最初のパラメータ.最初のパラメータは、渡されるパラメータの数を表します.
  • 次にva_を呼び出すarg()伝達パラメータリストva_リストと返されるパラメータのタイプ.va_arg()の戻り値は現在のパラメータです.
  • 再び、すべてのパラメータに対してva_を繰り返し呼び出すarg()
  • 最後にva_を呼び出すend()伝達va_Listは完了後のクリアに必要です.

  • va_arg()


    Syntax:
    #include <stdarg.h>
    type va_arg( va_list argptr, type );
    void va_end( va_list argptr );
    void va_start( va_list argptr, last_parm );

    Description:
    The va_arg() macros are used to pass a variable number of arguments to a function.
    1. First, you must have a call to va_start() passing a valid va_list and the mandatory first argument of the function. This first argument describes the number of parameters being passed.
    2. Next, you call va_arg() passing the va_list and the type of the argument to be returned. The return value of va_arg() is the current parameter.
    3. Repeat calls to va_arg() for however many arguments you have.
    4. Finally, a call to va_end() passing the va_list is necessary for proper cleanup.
    Example:
    int sum( int, ... );
    int main( void ) {
        
      int answer = sum( 4, 4, 3, 2, 1 );
      printf( "The answer is %d
    ", answer ); return( 0 ); } int sum( int num, ... ) { int answer = 0; va_list argptr; va_start( argptr, num ); for( ; num > 0; num-- ) answer += va_arg( argptr, int ); va_end( argptr ); return( answer ); } OUTPUT: Displays 10, which is 4+3+2+1.