printfにおける可変引数の実例 - 2024年度 システムプログラミング
printf における可変引数の実例
プログラム中で,
1: main() 2: { 3: char string[] = "ABC"; 4: myprintf("%d,%s", 10, string); 5: }
としたとき,
1: myprintf(char *fmt, ...) 2: { 3: ... 4: }
として宣言された myprintf
内にジャンプしたときのスタックの様子は,
以下のようになるでしょう.ただし,アドレスは,かならずしもこの値にはなりません.
アドレス | ラベル(変数名) | 中身 | 型 | 説明 |
---|---|---|---|---|
4976 | 新$sp→ | |||
: | : | : | : | : |
5000 | fmt | 6004 | char* | fmtが指す文字列の先頭アドレス |
5004 | 10 | int | 第2引数 | |
5008 | 6000 | char* | stringが指す文字列の先頭アドレス | |
5012 | 未使用 | 第4引数があれば,ここに入る | ||
: | : | : | : | : |
6000 | string | ABC\0 | string の中身 | |
6004 | 無名 | %d,% | ||
6008 | 無名 | s\0 | ||
: | : | : | : | : |
第2引数を取り出すには?
1: char *p = ((char*)&fmt) + ROUNDUP_SIZEOF(fmt); 2: 3: switch (*fmt){ 4: case 'd': 5: print_int(*(int*)p); 6: p = p + ROUNDUP_SIZEOF(int); 7: break; 8: case 's': 9: print_string(*(char**)p); 10: p = p + ROUNDUP_SIZEOF(char*); 11: break; 12: case 'c': 13: print_char(*(char*)p); 14: p = p + ROUNDUP_SIZEOF(char); 15: break;
ここで,ROUNDUP_SIZEOF… は fmt の sizeof を 4の倍数に切り上げる操作をする.ここの場合は, 4のままなので,
1: char *p = ((char*)&fmt) + 4; 2: 3: switch (*fmt){ 4: case 'd': 5: print_int(*(int*)p); 6: p = p + 4; 7: break; 8: case 's': 9: print_string(*(char**)p); 10: p = p + 4; 11: break; 12: case 'c': 13: print_char(*(char*)p); 14: p = p + 4; 15: break;
のように考えることができます.
fmt
中に %
を見付ける度に p
は 5004, 5008, ...
と,次の引数が入っているアドレスになることが分かります.
p
は,ポインタですが, p
が指すアドレスに入っている値は,
それぞれ %
の後の文字によって型が違ってきます.
この例では, %d
の場合は, int
型(10) なので, p
は
int
を指すアドレスつまり int*
ということになります.
従って,10を得るためには, p
を int*
にキャストし,
そのポインタが指す中身を取り出さなければなりません.
print_int(*(int*)p);
中の *(int*)p
は,それを表しています.
一方,次の引数 %s
に対応する 5008
番地にある 6000
という値は,
char
ではなく, char*
です.従って, p (つまり5008)
は,
char*
を指すポインタ (char**)
となるため,
(char*)p
ではなく, (char**)p
と書く必要があります.
print_string(*(char**)p);
中の *(char**)p
は,それを表しています.