27
int max(int n, ...)

私はcdecl呼び出し規約を使用しており、呼び出し先が戻った後に呼び出し元が変数をクリーンアップします。

va_endマクロがどのように機能するのか知りたいのva_startですがva_arg

呼び出し元は、引数の配列のアドレスをmaxの2番目の引数として渡しますか?

4

3 に答える 3

36

C 言語がスタックにパラメーターを格納する方法を見ると、マクロの動作方法が明確になるはずです。

Higher memory address    Last parameter
                         Penultimate parameter
                         ....
                         Second parameter
Lower memory address     First parameter
       StackPointer  ->  Return address

(ハードウェアによっては、スタック ポインターが 1 行下にあり、上位と下位が入れ替わることがあることに注意してください)

引数は、パラメーターの型がなくても、常にこの1のように格納されます。...

va_startマクロは、最初の関数パラメーターへのポインターをセットアップするだけです。

 void func (int a, ...)
 { 
   // va_start
   char *p = (char *) &a + sizeof a;
 }

これはp、2 番目のパラメーターを示しています。マクロはこれva_argを行います:-

 void func (int a, ...)
 { 
   // va_start
   char *p = (char *) &a + sizeof a;

   // va_arg
   int i1 = *((int *)p);
   p += sizeof (int);

   // va_arg
   int i2 = *((int *)p);
   p += sizeof (int);

   // va_arg
   long i2 = *((long *)p);
   p += sizeof (long);
 }

va_endマクロは、p値を に設定するだけNULLです。

ノート:

  1. 最適化コンパイラと一部の RISC CPU は、スタックを使用するのではなく、レジスタにパラメータを格納します。このパラメーターが存在すると、...この機能がオフになり、コンパイラーがスタックを使用できなくなります。
于 2012-09-11T14:27:07.517 に答える
10

引数がスタックに渡されると、va_「関数」 (ほとんどの場合、マクロとして実装されます) はプライベート スタック ポインターを操作するだけです。va_startこのプライベート スタック ポインタは、 に渡された引数から格納されva_arg、パラメータを反復するときに「スタック」から引数を「ポップ」します。

max次のように、3 つのパラメーターを指定して関数を呼び出すとします。

max(a, b, c);

関数内maxでは、スタックは基本的に次のようになります。

      +-----+
      | | c |
      | | b |
      | | | |
      | | 戻る |
SP -> +-----+

SPは実際のスタック ポインターであり、実際には ではなく、aスタック上にあるのではなく、それらの値です。関数が終了したときにジャンプする戻りアドレスです。bcret

va_start(ap, n)引数のアドレス (n関数プロトタイプ内) を取得し、そこから次の引数の位置を計算することで、新しいプライベート スタック ポインターを取得します。

      +-----+
      | | c |
ap -> | b |
      | | | |
      | | 戻る |
SP -> +-----+

これを使用するva_arg(ap, int)と、プライベート スタック ポインタが指すものを返し、プライベート スタック ポインタを次の引数を指すように変更して「ポップ」します。スタックは次のようになります。

      +-----+
ap -> | c |
      | | b |
      | | | |
      | | 戻る |
SP -> +-----+

もちろん、この説明は単純化されていますが、原理を示しています。

于 2012-09-11T14:13:35.577 に答える