int max(int n, ...)
私はcdecl
呼び出し規約を使用しており、呼び出し先が戻った後に呼び出し元が変数をクリーンアップします。
va_end
マクロがどのように機能するのか知りたいのva_start
ですがva_arg
?
呼び出し元は、引数の配列のアドレスをmaxの2番目の引数として渡しますか?
int max(int n, ...)
私はcdecl
呼び出し規約を使用しており、呼び出し先が戻った後に呼び出し元が変数をクリーンアップします。
va_end
マクロがどのように機能するのか知りたいのva_start
ですがva_arg
?
呼び出し元は、引数の配列のアドレスをmaxの2番目の引数として渡しますか?
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
です。
ノート:
...
この機能がオフになり、コンパイラーがスタックを使用できなくなります。引数がスタックに渡されると、va_
「関数」 (ほとんどの場合、マクロとして実装されます) はプライベート スタック ポインターを操作するだけです。va_start
このプライベート スタック ポインタは、 に渡された引数から格納されva_arg
、パラメータを反復するときに「スタック」から引数を「ポップ」します。
max
次のように、3 つのパラメーターを指定して関数を呼び出すとします。
max(a, b, c);
関数内max
では、スタックは基本的に次のようになります。
+-----+ | | c | | | b | | | | | | | 戻る | SP -> +-----+
SP
は実際のスタック ポインターであり、実際には ではなく、a
スタック上にあるのではなく、それらの値です。関数が終了したときにジャンプする戻りアドレスです。b
c
ret
va_start(ap, n)
引数のアドレス (n
関数プロトタイプ内) を取得し、そこから次の引数の位置を計算することで、新しいプライベート スタック ポインターを取得します。
+-----+ | | c | ap -> | b | | | | | | | 戻る | SP -> +-----+
これを使用するva_arg(ap, int)
と、プライベート スタック ポインタが指すものを返し、プライベート スタック ポインタを次の引数を指すように変更して「ポップ」します。スタックは次のようになります。
+-----+ ap -> | c | | | b | | | | | | | 戻る | SP -> +-----+
もちろん、この説明は単純化されていますが、原理を示しています。