ルギア君の戯言

雑多な記事。

printf のフシギ

printf 関数は、

int printf(const char *format, ...);

の形をしていますが、 ... の部分は format を読み込んで型を決めるわけですよね。


さて、その ... の部分を読み込む時、普通は

void va_start(va_list ap, last);
type va_arg(va_list ap, type);
void va_end(va_list ap);
void va_copy(va_list dest, va_list src);

の関数群を使うんだが、これらには format の後から順に読んでいかなければならないという制限があるのは知っていると思う。


printf 関数では

printf("%3$d %2$d %1$d\n", 1, 2, 3);

とやると、

3 2 1

って後ろから引数を使うことができるんですよ。え? できない? それは VC++ や BCC だからでしょうね。このことは SUS (Single UNIX Specification) に規定されているので、UNIX (と互換のある Linux)下では使えることが保証されています。C99 では定義されていないので(man 参照)、VC++ や BCC では使えないんでしょうね。(そもそも BCC は C99 にすら対応しているのかハテナですが)


もし、前から読みながら埋めていけば合理的だし、処理も楽だけど、上のせいで SUS 互換の printf 関数を作ろうとなると、非常に悩むところです。
va_arg では前から読むことしか許されていませんから、呼び出し(%...)があるたびに順に前から読んで探していかなければ行けませんが、最初の時点では、型が知らないとどうしようもないんだよね。
この UNIX の printf はどうやって実装しているのかなぁ? glibc のソースを読むとわかったりするのかな? (ぁ


っていうか読んでみるか。


ちなみに、$ を使って引数の場所を指定する場合は、何回でも呼び出すことができるんだ。

printf("%1$d %1$d %1$d\n", 1);

の出力は

1 1 1

だ。


というわけで OmoiKondara に(正確には spec ファイルに)お願いして glibc のソースコードを取ってきてもらいました(笑)
っていうか pkgs ディレクトリにすでにあったのねw


果たして見付かるのでしょうかw


出てきたぜw

/* Copyright (C) 1991, 1997, 2002, 2004, 2006 Free Software Foundation, Inc.
   This file is part of the GNU C Library. 
 */

int
__printf (const char *format, ...)
{
  va_list arg;
  int done;

  va_start (arg, format);
  done = vfprintf (stdout, format, arg);
  va_end (arg);

  return done;
}

vfprintf を呼んでいるだけ。
まあ、そりゃそうですよねw


というわけで、vfprintf を見に行きましょうw
って言って見付かったんだが、長いw
というわけで終わりw