printf
出典: フリー百科事典『ウィキペディア(Wikipedia)』 (2024/07/06 14:45 UTC 版)
コード例
#include <stdio.h>
int main(void)
{
int a = 1234;
printf("%d %o %x\n", a, a, a);
printf("%s %c\n", "abc", 'x');
return 0;
}
上記のコードをコンパイルし実行すると、次の出力が得られる。
1234 2322 4d2 abc x
脆弱性
一般に、書式文字列を外部や利用者から自由に指定できる状態とすることはセキュリティホールの温床となるため望ましくない。特に%n
書式を用いたものが有名である[8]。Visual C++のCRTでは既定で%n
書式が無効化されており、使用できなくなっている[9]。
また、書式化文字列中の書式指定子と実引数の数や型が正しく対応していない場合は未定義動作を引き起こす。Cの可変長引数は型消去によって実現される仕組みであるため、printf()
の内部では書式と実引数の数や型の不一致を検出することができず、本質的に安全性が欠如している。正しい書式を与えることはプログラマ側の責任である。
なお、引数で受け取ったバッファに書式化された文字列を出力する関数群は、バッファサイズを超えてデータを書き込んでしまうと未定義動作を引き起こす。特に、sprintf()
やvsprintf()
といったバッファサイズ(書き込み可能な最大サイズ)を指定することのできない関数は潜在的なバッファオーバーフローの脆弱性を抱えているため使うべきではなく、代わりにC99で追加されたsnprintf()
やvsnprintf()
を使うべきである。
他言語
C言語から派生したC++やD言語はもとより、PHP[10]、Ruby[11]、Perlなど他の言語でもprintfが実装されている。Python 2.xではprint
文だったが、3.xではprint
関数となった。
また、Ruby[12]やPythonなど、%
演算子によってsprintf()
相当機能をサポートするものも存在する。Boost C++ライブラリのBoost.Format[13]では、boost::format
クラスの%
演算子オーバーロードによって類似の機能を実現している[14]。
C++11ではC99のライブラリ関数の多くが取り込まれた。C++20では関数テンプレートstd::format()
[15]が、C++23ではstd::print()
[16]が追加された。
Unix系オペレーティングシステムのコマンドとして実装されたprintfもある。外部コマンドのほか、Bashなど一部のUnixシェルではビルトインで実装されている。
Java
Java 1.5ではString
クラスにformat()
メソッドが、java.io.PrintStream
クラスにformat()
メソッドとprintf()
メソッドが追加された。
標準出力の場合は、例えば以下のように呼び出すことができる。
System.out.printf("Integer = 0x%02X, Double = %.4f\n", 10, Math.PI);
Javaの可変長引数はObject
の配列だが、int
やdouble
のようなプリミティブ型の引数はオートボクシング(自動ボックス化)によってプリミティブラッパークラスにラップされて渡されるため、オーバーヘッドはあるものの本質的に型安全である。書式指定に誤りがあった場合はjava.util.IllegalFormatException
例外を、また書式指定に対応する実引数が不足していた場合はjava.util.MissingFormatArgumentException
例外をスローする。
Javaに限らず、後発のオブジェクト指向言語や動的型付け言語の場合は、可変長引数として使われるコレクション(配列やリスト)が長さ情報を持っており、またコレクション中の各オブジェクトが自分自身の型情報を持っているため、printf()
相当機能がバッファ安全かつ型安全に実現できるようになっている。
注釈
- ^ 実際のローカライズ作業では、gettext等によってメッセージカタログから文字列を得るようにコードを記述する。
- ^ 類似の順序指定可能な書式化機能を持つものとして、Windows APIのFormatMessage()関数や、Microsoft Foundation ClassのAfxFormatString2()関数、.NET FrameworkのSystem.String.Format()メソッドなどが挙げられる。
- ^ 可変長引数では「既定の実引数拡張」により、float型の引数はdouble型へと変換されるため、本来はdoubleに対して修飾子を適用する必要はない。
- ^ 例えばLP64環境では
long
は64ビットであり、仮にint64_t
型の実引数に対して%ld
書式を使用したとしてもほとんどの実装では期待通り動作する可能性が高いが、LLP64環境ではlong
は32ビットであり、int64_t
型の実引数に対して%ld
書式を使用すると未定義動作を引き起こす。また、long long
は少なくとも64ビット以上の値を表現できることが保証されているが、int64_t
と同じ型であるという保証はどこにもなく、int64_t
型の実引数に対して%lld
書式を使用した場合の動作はやはり未定義となる。同様に、int
とint32_t
が同じ型であるという保証もない。 - ^ Microsoft Visual C++には非標準関数としてバッファサイズを受け取らないswprintfが存在するが、バージョン2005 (8.0) 以降では既定で無効化されており、非推奨となっている[3][4]。
出典
- ^ Fixed width integer types (since C99) - cppreference.com
- ^ 固定幅の整数型 (C99以上) - cppreference.com
- ^ sprintf、swprintf (CRT) | Microsoft Docs
- ^ sprintf, _sprintf_l, swprintf, _swprintf_l, __swprintf_l | Microsoft Docs
- ^ wsprintfA function (winuser.h) - Win32 apps | Microsoft Learn
- ^ wnsprintfA function (shlwapi.h) - Win32 apps | Microsoft Learn
- ^ セーフ文字列関数の使用 - Windows drivers | Microsoft Learn
- ^ “第10章 著名な脆弱性対策 フォーマット文字列攻撃対策”. IPA ISEC セキュア・プログラミング講座:C/C++言語編. 独立行政法人 情報処理推進機構. 2012年7月3日閲覧。
- ^ Format Specification Syntax: `printf` and `wprintf` Functions | Microsoft Learn
- ^ “PHP: printf”. PHPマニュアル (2013年3月22日). 2013年3月28日閲覧。
- ^ “Kernel.#printf”. Ruby 1.9.3 リファレンスマニュアル. 2013年3月28日閲覧。
- ^ “Instance method String#%”. Ruby 1.9.3 リファレンスマニュアル. 2013年3月28日閲覧。
- ^ “The Boost Format Library” (2006年12月2日). 2013年3月28日閲覧。
- ^ 文字列フォーマット - boostjp
- ^ format - cpprefjp C++日本語リファレンス
- ^ print - cpprefjp C++日本語リファレンス
固有名詞の分類
- printfのページへのリンク