printf
printf(プリントエフ)は、C言語の標準ライブラリに属し、ヘッダー (stdio.h
) で宣言されている関数である。引数で与えられた書式付きの文字列を、環境によって設定された標準出力 (stdout
) に出力する。その機能はJIS X 3010:2003において
と規定されている (7.19.6.3)。
printf
関数は,与えられた実引数の前にstdout
を実引数として付加したfprintf
関数と等価とする。
この関数は、第1引数に与えられた文字列を出力する。C言語の他の単純な入出力関数に比べ、比較的複雑な構造を持っており、第1引数の文字列のなかで書式を指定することで、第2引数以降の任意の数の引数(可変長引数)を、書式に従って出力することができる。また、整数型(int
型)の戻り値を持ち、出力に成功した場合には転送したバイト数、出力に失敗した場合には負数を返却する。
形式[編集]
#include <stdio.h>
int printf(const char * restrict format, ...);
書式化文字列[編集]
上記形式における第一引数には、それに続く実引数の変換方法を指定する。書式化文字列には、通常のマルチバイト文字または%
で始まる変換指定のいずれかの指令を0個以上含む。マルチバイト文字が含まれ、かつ文字コードがシフトシーケンスに依存する場合には、書式化文字列は初期シフト状態で始まり、初期シフト状態で終わらなければならない。書式指定を行う%〜
それぞれを書式指定子と呼ぶ。
変換指定[編集]
変換指定は次の形式をとる。([ ]内は省略可能)
%[引数順][フラグ][最小フィールド幅][.精度][長さ修飾子]変換指定子
引数順 (POSIX)[編集]
これは標準C規格の仕様ではなく、POSIXで規定されている拡張である。
書式文字列において%
の代わりに%m$
を記述することで、続く可変長引数のうちどれを使うかを番号mで指定できる。
例えば、
const char* fmt = "Invalid command %1$s at line %2$d.\n";
const char* cmd = "hoge";
const int lineNumber = 20;
printf(fmt, cmd, lineNumber);
とした場合、
Invalid command hoge at line 20.
と表示される。これだけならば大きな意味はないが、例えばメッセージを翻訳(ローカライズ)する際、
const char* fmt = "%2$d行目のコマンド %1$s は不正です。\n";
というように書式文字列だけをローカライズする[1]ことで、可変長引数の指定順を変更せずに、
20行目のコマンド hoge は不正です。
という自然な語順の出力を得ることができる[2]。
フラグ[編集]
変換指定のフラグは以下の通り。
フラグ | 意味 |
---|---|
- | フィールドの左寄せ |
+ | 常に符号を出力 |
空白 | 数値が正または 0 の場合は符号の代わりに空白を出力 |
# | 代替形式。基数を表すプレフィックスの出力等 |
0 | 出力文字数が最小フィールド幅未満の場合は'0'を出力 |
長さ修飾子[編集]
修飾子 | 意味 | 導入バージョン |
---|---|---|
hh | 実引数は char 型 | C99以降 |
h | 実引数は short 型 | 全バージョン |
l(エル) | 実引数は long 型または wchar_t 型または double 型[3] | wchar_t についてはC95以降、double についてはC99以降 |
ll(エルエル) | 実引数は long long 型 | C99以降 |
j | 実引数は intmax_t 型 | C99以降 |
z | 実引数は size_t 型 | C99以降 |
t | 実引数は ptrdiff_t 型 | C99以降 |
L | 実引数は long double 型 | 全バージョン |
なお、この引数型はデータ型モデルによってint32_tなどのビット数が指定される型に対して移植の際に64ビットアーキテクチャと32ビットアーキテクチャでは非互換が生じることがある。これに対応するためには、明示的な型キャストや、inttypes.hヘッダに定義されるPRIで始まるマクロを利用する。
変換指定子[編集]
指定子 | 意味 | 導入バージョン |
---|---|---|
d, i | 10進符号付き整数 | 全バージョン |
u | 10進符号無し整数 | 全バージョン |
o | 8進符号無し整数 | 全バージョン |
x, X | 16進符号無し整数( X は大文字で出力) | 全バージョン |
e, E | 指数形式浮動小数点数( E は大文字で出力) | 全バージョン |
f, F | 小数形式浮動小数点数( F は大文字で出力) | 全バージョン |
g, G | e または f 形式のうち適した方( G は大文字で出力) | 全バージョン |
a, A | 16進浮動小数点( A は大文字で出力) | C99以降 |
c | 文字 | 全バージョン |
s | 文字列 | 全バージョン |
p | ポインタの値 | 全バージョン |
n | 整数変数に出力済み文字数を格納 | 全バージョン |
% | '%'の出力 | 全バージョン |
関連する関数[編集]
fprintf[編集]
fprintfは、引数にファイルポインタfpが追加され、標準出力の代わりにfpへ出力する変種である。
#include <stdio.h>
int fprintf(FILE * restrict fp, const char * restrict format, ...);
sprintf, snprintf[編集]
sprintfとsnprintfは、引数にchar配列の要素へのポインタstrが追加されたもので、標準出力の代わりにstrへ出力する変種である。snprintfは、さらにstrに書き込んで良い文字数を指定する引数が追加されたものであるが、Cの標準規格に収録されたのはC99からである。
#include <stdio.h>
int sprintf(char * restrict str, const char * restrict format, ...);
int snprintf(char * restrict str, size_t size, const char * restrict format, ...);
wprintf, fwprintf, swprintf[編集]
wprintf, fwprintf, swprintfは、それぞれprintf, fprintf, snprintfに対応し、ワイド文字を使用するものである。sprintfに対応するワイド文字関数(出力バッファサイズを受け取らない関数)は存在しない[4]。
#include <stdio.h>
int wprintf(const wchar_t * restrict format, ...);
int fwprintf(FILE * restrict fp, const wchar_t * restrict format, ...);
int swprintf(wchar_t * restrict str, size_t size, const wchar_t * restrict format, ...);
va_listを引数に取るもの[編集]
ここまでに挙げたprintfとその変種に対して、可変個引数部分をva_listに変化させた種類が存在する。それぞれ、関数名の頭にvを付けた名称となっている。
#include <stdio.h>
int vprintf(const char * restrict format, va_list args);
int vfprintf(FILE * restrict fp, const char * restrict format, va_list args);
int vsprintf(char * restrict str, const char * restrict format, va_list args);
int vsnprintf(char * restrict str, size_t size, const char * restrict format, va_list args);
int vwprintf(const wchar_t * restrict format, va_list args);
int vfwprintf(FILE * restrict fp, const wchar_t * restrict format, va_list args);
int vswprintf(wchar_t * restrict str, size_t size, const wchar_t * restrict format, va_list args);
安全性を向上させたもの[編集]
C11のAnnex K Bounds‐checking interfacesで規定された、安全性を向上させた種類が存在する。それぞれ、関数名の末尾に_s
を付けた名称となっている。これらの関数は、__STDC_WANT_LIB_EXT1__
を1
に定義した上で対応するヘッダをインクルードすると宣言される。
#define __STDC_WANT_LIB_EXT1__ 1
#include <stdio.h>
#include <wchar.h>
int printf_s(const char * restrict format, ...);
int fprintf_s(FILE * restrict fp, const char * restrict format, ...);
int sprintf_s(char * restrict str, rsize_t size, const char * restrict format, ...);
int snprintf_s(char * restrict str, rsize_t size, const char * restrict format, ...);
int vprintf_s(const char * restrict format, va_list arg);
int vfprintf_s(FILE * restrict fp, const char * restrict format, va_list arg);
int vsprintf_s(char * restrict str, rsize_t size, const char * restrict format, va_list arg);
int vsnprintf_s(char * restrict str, rsize_t size, const char * restrict format, va_list arg);
int wprintf_s(const wchar_t * restrict format, ...);
int fwprintf_s(FILE * restrict fp, const wchar_t * restrict format, ...);
int swprintf_s(wchar_t * restrict str, rsize_t size, const wchar_t * restrict format, ...);
int snwprintf_s(wchar_t * restrict str, rsize_t size, const wchar_t * restrict format, ...);
int vwprintf_s(const wchar_t * restrict format, va_list arg);
int vfwprintf_s(FILE * restrict fp, const wchar_t * restrict format, va_list arg);
int vswprintf_s(wchar_t * restrict str, rsize_t size, const wchar_t * restrict format, va_list arg);
int vsnwprintf_s(wchar_t * restrict str, rsize_t size, const wchar_t * restrict format, va_list arg);
以下のような機能が追加されている。
FILE*
や文字列の引数(%s
に対応する可変長引数部分の引数も含む)に対するNULLポインタチェック%n
書式の禁止
Windows API[編集]
Microsoft Windows用のWindows APIには、sprintf()
やsnprintf()
などによく似た名前の関数として以下のようなAPIが用意されている[5][6]。
// winuser.h
int wsprintfA(LPSTR unnamedParam1, LPCSTR unnamedParam2, ...);
int wsprintfW(LPWSTR unnamedParam1, LPCWSTR unnamedParam2, ...);
int wvsprintfA(LPSTR unnamedParam1, LPCSTR unnamedParam2, va_list arglist);
int wvsprintfW(LPWSTR unnamedParam1, LPCWSTR unnamedParam2, va_list arglist);
// shlwapi.h
int wnsprintfA(PSTR pszDest, int cchDest, PCSTR pszFmt, ...);
int wnsprintfW(PWSTR pszDest, int cchDest, PCWSTR pszFmt, ...);
int wvnsprintfA(PSTR pszDest, int cchDest, PCSTR pszFmt, va_list arglist);
int wvnsprintfW(PWSTR pszDest, int cchDest, PCWSTR pszFmt, va_list arglist);
ここで、LPSTR
とPSTR
はchar*
、LPCSTR
とPCSTR
はconst char*
、LPWSTR
とPWSTR
はwchar_t*
、LPCWSTR
とPCWSTR
はconst wchar_t*
に相当するtypedef型エイリアスである。Wサフィックスはワイド文字によるUnicode文字セット、AサフィックスはANSIマルチバイト文字セット(MBCS)を意味する。プロジェクトのUnicode対応設定(マクロシンボルUNICODE
の定義有無)によって関数の実装がW版またはA版に切り替わるwsprintf
マクロやwnsprintf
マクロなども用意されている。
いずれもfloat
やdouble
といった浮動小数点数型の書式化には対応しておらず、C/C++の標準ライブラリ関数とは互換性がない。Cランタイムライブラリ (CRT) に依存せず、Windows環境限定で標準的に使えることがメリットだが、これらの使用は推奨されておらず、<strsafe.h>
で定義されているセーフ文字列関数の使用が推奨されている[7]。
なお、Windows API関数の呼び出し規約は、ほぼすべて_stdcall
(__stdcall
、WINAPI
/ STDAPICALLTYPE
)であるが、上記のうち可変長引数...
を受け取るAPI関数は例外的に_cdecl
(__cdecl
、WINAPIV
/ STDAPIVCALLTYPE
)となっている。x86の場合は引数の受け渡し動作が呼び出し規約に左右される。
コード例[編集]
#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()
の内部では書式と実引数の数や型の不一致を検出することができず、本質的に安全性が欠如している。正しい書式を与えることはプログラマ側の責任である。
他言語[編集]
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++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に対して修飾子を適用する必要はない。
- ^ Microsoft Visual C++には非標準関数としてバッファサイズを受け取らないswprintfが存在するが、バージョン2005 (8.0) 以降では既定で無効化されており、非推奨となっている。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++日本語リファレンス
参考文献[編集]
関連項目[編集]
外部リンク[編集]
- UNIX コマンド
- printf(1) man page GNU 版。JM Project
- printf(1) man page (Solaris 10 Reference Manual) (英語)
- printf(1) man page(HP-UX リファレンス)
- printf The Open Group Base Specifications Issue 7, 2018 edition
- Bashビルトイン: Bash Builtins (Bash Reference Manual)
- 標準 C ライブラリ
- printf(3) JM Project
- printf(3C) man page (Solaris 10 Reference Manual)
- printf(3S) man page(HP-UX リファレンス)