go-printf - 背景

歴史的経緯

printf の起源

printf は1970年代初頭、Ken Thompson によって UNIX オペレーティングシステムのために開発されました。最初は B 言語で実装され、その後 Dennis Ritchie によって C 言語に移植されました。

名前の由来は "print formatted" で、フォーマット文字列に従ってデータを整形して出力する機能を提供します。

フォーマット文字列の進化

1972: C言語の printf 誕生
1989: ANSI C で標準化
1999: C99 で %lld, %zu 追加
2011: C11 で %a (16進浮動小数点) 追加

各言語での printf 派生

言語 関数/メソッド 特徴
C `printf()` オリジナル
Python `%` 演算子, `format()` 型安全でない/型安全
Java `String.format()` 型チェックなし
Rust `format!()` コンパイル時チェック
Go `fmt.Printf()` リフレクション使用

コンピュータサイエンス的な意味

フォーマット文字列の解析

printf のフォーマット文字列解析は、有限状態機械(FSM)で実装されます:

状態遷移図:
[通常] --'%'--> [開始] --フラグ--> [フラグ] --数字--> [幅]
                  |                    |              |
                  v                    v              v
              [指定子]            [指定子]      [精度開始]
                  |                    |              |
                  v                    v              v
              [出力]               [出力]        [精度]
                                                     |
                                                     v
                                                 [指定子]
                                                     |
                                                     v
                                                  [出力]

進数変換アルゴリズム

整数を任意の基数に変換するアルゴリズム:

function toBase(n, base):
    if n == 0:
        return "0"

    digits = ""
    while n > 0:
        remainder = n % base
        digits = digitChar(remainder) + digits
        n = n / base

    return digits

可変長引数の実装

C では可変長引数はスタックベースで実装されています:

// C の実装
void printf(const char *fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    // va_arg(ap, type) でスタックから取得
    va_end(ap);
}

Go では interface{} のスライスとして実現されています:

func Printf(format string, args ...interface{}) {
    // args は []interface{} として扱える
    for _, arg := range args {
        // 型スイッチで処理
        switch v := arg.(type) {
        case int:
            // ...
        }
    }
}

実践での活用

ログ出力

log.Printf("[%s] %s: %v", timestamp, level, message)

デバッグ

fmt.Printf("DEBUG: x=%d, y=%d, result=%v\n", x, y, result)

文字列フォーマット

filename := fmt.Sprintf("%s_%04d.txt", prefix, index)

課題と実世界のギャップ

1. 型安全性

Go の標準 fmt パッケージはリフレクションを使用しているため、コンパイル時の型チェックがありません。Rust の format!() マクロはコンパイル時にチェックします。

2. パフォーマンス

リフレクションを使用する printf はパフォーマンスオーバーヘッドがあります。高性能が必要な場面では、直接的な文字列構築が推奨されます。

3. セキュリティ

フォーマット文字列攻撃は、ユーザー入力をフォーマット文字列として使用すると発生します:

// 危険(Cの場合)
printf(userInput);  // userInput が "%s%s%s" なら脆弱性

// 安全
printf("%s", userInput);

Go は比較的安全ですが、情報漏洩の可能性は残ります。

関連する標準規格

  • IEEE 754 - 浮動小数点数の表現
  • ISO/IEC 9899 - C言語標準
  • POSIX printf - UNIX互換仕様