stdioは「standard input/output」の略で、日本語では標準入出力と呼びます。読み方は「スタンダード・アイオー」です。同じ語が、C言語のヘッダーファイル <stdio.h>、プログラムの起動時に開いている3本のストリーム(stdin・stdout・stderr)、MCPのように標準入出力でプロセス同士を通信させる方式、の3つの文脈で使われます。
この記事では読み方と3つの意味を整理したうえで、「printfの出力が遅れる・順番が入れ替わる」原因になるバッファリングを、macOS 26.6.2とApple clang 21.0.0で実行した結果つきで解説します。後半ではD言語の標準ライブラリ std.stdio の使い方を、DMD 2.113.1で動かしたコードで紹介します。
まとめ|stdioで押さえる5点
- 意味と読み方:standard input/output(標準入出力)の略。「スタンダード・アイオー」と読みます。ノーコードWeb制作ツールのSTUDIO(スタジオ)とは別の語です。
- 3本のストリーム:標準入力stdin・標準出力stdout・標準エラー出力stderrは、起動時にファイルディスクリプタ0・1・2として開いています。
- C言語のstdio.h:printf・scanf・fopenなどを宣言するヘッダーです。省くと、Apple clang 21ではC89指定でもコンパイルエラーになりました。
- バッファリング:Linux・macOSのstdoutは端末なら行単位、パイプやファイルへ出すとまとめて書き出されます。stderrとの出力順が入れ替わったり、異常終了で出力が消えたりするので、必要な箇所で
fflush(stdout)を呼びます。 - MCPのstdio:クライアントがサーバーを子プロセスとして起動し、stdin・stdoutで1行1件のJSON-RPCをやり取りします。ログはstdoutではなくstderrへ書きます。
stdioの読み方と意味|standard input/outputの略
stdioの読み方|略語の展開と文字読み
stdioはstandard(標準)とI/O(Input/Output=入出力)をつないだ略語です。読み方は規格で決められておらず、単語に戻して「スタンダード・アイオー」と読むか、1文字ずつ「エス・ティー・ディー・アイ・オー」と読むのが一般的です。ヘッダー名の stdio.h は「スタンダード・アイオー・ドット・エイチ」と読みます。
綴りが似たノーコードWeb制作ツールの「STUDIO(スタジオ)」とは別の語です。「stdio」で検索するとSTUDIOの公式サイトが上位に出ることがあり、「スタジオ」と読み違えられる原因になっています。
stdioの使用文脈|C言語・OS・MCP・D言語
「stdio」が何を指すかは、話の文脈で決まります。プログラミングの入門で出てくるのはC言語のヘッダーで、AIエージェントの設定で出てくるのはMCPの通信方式です。
| 文脈 | stdioが指すもの | 出てくる場面 |
|---|---|---|
| C言語 | 標準入出力ライブラリのヘッダー stdio.h | #include <stdio.h> |
| OS・シェル | stdin・stdout・stderrの3本のストリーム | リダイレクト、パイプ |
| MCP・CLI連携 | 標準入出力を使うプロセス間通信の方式 | "type": "stdio" の設定 |
| D言語など | 標準ライブラリのモジュール名 | import std.stdio; |
共通するのは、起動時から開いている入力1本と出力2本(標準ストリーム)です。C言語のstdio.hやD言語のstd.stdioは、それに加えてプログラムが開くファイルの入出力も扱います。以下、標準ストリームから順に説明します。
stdin・stdout・stderrの仕組み|ファイルディスクリプタ0・1・2
通常のUNIX系プログラムでは、親プロセスから引き継いだファイルディスクリプタ0・1・2に、Cランタイムが標準入力・標準出力・標準エラー出力のストリームを対応付けます。Linuxのman-pages stdin(3) には、起動時のstdin・stdout・stderrのファイルディスクリプタがそれぞれ0・1・2で、<unistd.h> の STDIN_FILENO・STDOUT_FILENO・STDERR_FILENO がこの値で定義されていると書かれています。この番号の決まりは、UNIX系OSの共通仕様であるPOSIXに由来します。
| 名前 | 日本語 | 番号 | 既定の接続先 | 書くもの |
|---|---|---|---|---|
| stdin | 標準入力 | 0 | キーボード | (読み取り専用) |
| stdout | 標準出力 | 1 | 画面 | 処理結果 |
| stderr | 標準エラー出力 | 2 | 画面 | エラー・進捗・ログ |
C言語のコードからPOSIXの関数 fileno() で番号を確かめると、macOS 26.6.2でも 0 1 2 が返りました。
#include <stdio.h>
#include <unistd.h>
int main(void)
{
printf("%d %d %d\n", fileno(stdin), fileno(stdout), fileno(stderr));
printf("%d %d %d\n", STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO);
return 0;
}
0 1 2
0 1 2
リダイレクトとパイプ|接続先の変更とフィルタの連結
シェルは、起動するプログラムの0・1・2番をファイルや別のプログラムへつなぎ替えられます。< と > がリダイレクト、| がパイプです。標準入力から読み、加工して標準出力へ書くだけのプログラムを「フィルタ」と呼び、Unixではsort・grep・wcのような小さなフィルタをパイプでつないで処理を組み立てます。
# 入力をファイルから読み、結果をファイルへ書く
sort < names.txt > sorted.txt
# エラーだけを別ファイルへ(2番をつなぎ替える)
make 2> build-error.log
# 結果とエラーを1つのファイルへまとめる
make > build.log 2>&1
# 前のプログラムの標準出力を、次の標準入力へ渡す
grep ERROR app.log | sort | uniq -c
2>&1 は「2番を、今の1番と同じ行き先にする」という意味です。make 2>&1 > build.log と順番を逆にすると、2番は元の画面につながったままになります。
stderrの役割|処理結果と診断メッセージの分離
出力が2本あるのは、パイプやリダイレクトで流す処理結果に、エラーや進捗の文字列を混ぜないためです。grep ERROR app.log | sort の途中でgrepがファイルを開けなくても、そのメッセージはstderrから画面に出るので、sortの入力は汚れません。自作のコマンドラインツールでも、結果はstdout、それ以外はstderrに書くのが基本です。ログをJSONで機械可読にする場合も、出力先はstderrかファイルにします(構造化ログの出力先の考え方と同じです)。
C言語のstdio.h|#include <stdio.h>で使える関数
stdio.h はC言語の標準入出力ライブラリのヘッダーファイルです。WG14がC23(ISO/IEC 9899:2024)の参照用に挙げる作業草案 N3220(位置付けはC2yの初期草案)の7.23節が、このヘッダーで宣言する関数と、stdin・stdout・stderr・FILE 型・EOF などを定めています。
| 分類 | 主な関数 |
|---|---|
| 書式付き入出力 | printf, scanf, fprintf, snprintf |
| 文字・文字列の入出力 | getchar, putchar, fgets, fputs, puts |
| ファイル操作 | fopen, fclose, remove, rename |
| バイナリ入出力 | fread, fwrite |
| 位置・バッファ | fseek, ftell, fflush, setvbuf |
| エラー処理 | ferror, clearerr, perror |
文字列を1行読む関数は fgets を使います。バッファの大きさを渡せない gets はC11で規格から削除されました。
stdio.hの省略|未宣言の関数呼び出しとコンパイル診断
#include は、指定したファイルの中身をその場所に取り込む指示です。stdio.h にはprintfの引数と戻り値の型(プロトタイプ宣言)が書かれていて、これを取り込まないとコンパイラはprintfの正しい呼び方を知りません。「おまじない」と説明されがちですが、実際は関数の宣言を読み込む処理です。
未宣言の関数呼び出しをエラーにするかは、処理系・言語モード・診断オプションによって異なります。次のコードをApple clang 21.0.0でコンパイルすると、-std=c17 でも -std=c89 でもエラーになり、実行ファイルは作られませんでした。
int main(void)
{
printf("hello\n");
return 0;
}
$ cc -std=c17 noinc.c -o noinc
noinc.c:3:5: error: call to undeclared library function 'printf' with type 'int (const char *, ...)'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
noinc.c:3:5: note: include the header <stdio.h> or explicitly provide a declaration for 'printf'
GCCも14から、宣言されていない関数の呼び出しを既定でエラーにし、出力ファイルを作りません。GCC 14の移行ガイドによると、標準Cに直せないソースは -std=c89・-std=gnu89・-fpermissive を付ければコンパイルできるので、GCCではC89モードの扱いがApple clangと異なります。古いソースをビルドしてこのエラーが出たら、オプションで抑えるより、該当するヘッダーを足すのが正しい直し方です。
バッファリング|printfの出力が遅れる・順番が入れ替わる原因
stdioの関数は、書いた文字をすぐOSに渡さず、いったんメモリ上のバッファにためます。いつ書き出すかの方式は3つあります。
- 行バッファ:改行を書いたときや、バッファがいっぱいになったときなどに書き出す
- フルバッファ:バッファがいっぱいになるか、
fflush・正常終了のときに書き出す - バッファなし:書くたびに書き出す
標準ストリームの初期バッファリング|C規格の規定と実装差
C23の作業草案 N3096 の7.23.3が定めるのは、起動時のstderrをフルバッファにしないことと、stdin・stdoutは対話的な機器(端末)を指していないと判定できる場合に限りフルバッファにすること、の2点です。「端末なら行バッファ」は規格ではなく実装の挙動です。
- Linux(glibc)・macOS:stdio(3) は、端末を指す出力ストリームが既定で行バッファになると説明しています。パイプやファイルへ出すとフルバッファです。
- Windows:Microsoftの setvbufのドキュメント には、Win32では
_IOLBF(行バッファ)が_IOFBF(フルバッファ)と同じ動作になると書かれています。一方、既定の状態についてStream I/Oの説明は、stdout・stderrはバッファがいっぱいになったとき、文字デバイス(コンソール)へ書く場合はライブラリ関数を呼ぶたびに書き出されるとしています。コンソールなら改行を待たずに表示されますが、ファイルやパイプへ出すと改行では書き出されません。
端末・パイプ・ファイル出力|stdoutとstderrの表示順の比較
stdoutとstderrに交互に書くだけのプログラムで、出力先を変えて順番を比べました。
#include <stdio.h>
int main(void)
{
printf("1: stdout\n");
fprintf(stderr, "2: stderr\n");
printf("3: stdout\n");
return 0;
}
| 実行のしかた | 表示された順 |
|---|---|
| 端末でそのまま実行 | 1 → 2 → 3 |
./buf 2>&1 | cat |
2 → 1 → 3 |
./buf > out.txt 2>&1 |
2 → 1 → 3 |
パイプやファイルへ出すとstdoutはフルバッファになり、1と3は終了時にまとめて書き出されます。stderrはバッファなしなので、2だけが先に届きます。CIのログで「エラーメッセージが、その原因になった処理の出力より前に出ている」ように見える場合、このバッファリングが原因の1つとして考えられます。
出力が消える場合もあります。printf("before crash\n") の直後にNULLポインタへ書き込んでセグメンテーションフォルトを起こすと、端末では「before crash」が表示されましたが、パイプやファイルへ出したときは0バイトでした。MicrosoftのStream I/Oの説明も、異常終了では出力バッファが書き出されずデータが失われることがあると注意しています。printfの出力をファイルやパイプへ流してデバッグしていると、クラッシュ直前のログが残らないのはこのためです。
出力遅延への対処|fflush・setvbuf・Pythonの-u
順番や即時性が必要な箇所では、書き出しを明示します。次の方法1と方法2は選択肢です。方法2の効果を確かめる場合は、setvbufの行を外してください。
#include <stdio.h>
int main(void)
{
setvbuf(stdout, NULL, _IONBF, 0); // 方法1: stdoutをバッファなしにする(最初の出力より前に呼ぶ)
printf("処理中...");
fflush(stdout); // 方法2: 必要な箇所で書き出す
return 0;
}
バッファなしでは出力をまとめる効果が失われ、OSへの書き込み回数が増えることがあります。大量に出力するプログラムでは、進捗表示やデバッグ出力など即時に見えることが必要な箇所だけ fflush を呼ぶ方法が向いています。
Pythonも同じ考え方です。sys.stdout は対話的なときは行バッファ、それ以外はブロックバッファで、python -u か環境変数 PYTHONUNBUFFERED(空でない文字列)でstdoutとstderrをバッファなしにできます。Dockerコンテナ内のPythonでログが遅れて出るときは、まずこれを疑います。
MCPのstdioトランスポート|通信とログの出力先の分離
MCP(Model Context Protocol)は、AIアプリとツールをつなぐためのプロトコルです。通信方式(トランスポート)はstdioとStreamable HTTPの2つが標準です。2026-07-28版の仕様の stdioトランスポート では、次のように定めています。
- クライアントがMCPサーバーを子プロセスとして起動し、サーバーはstdinからJSON-RPCメッセージを読み、stdoutへ書く
- メッセージは改行で区切り、メッセージの中に改行を含めてはならない(MUST NOT)
- サーバーはstdoutに、有効なMCPメッセージ以外を書いてはならない(MUST NOT)
- サーバーはログをUTF-8でstderrへ書いてよく(MAY)、クライアントはstderrの出力をエラーと見なすべきではない(SHOULD NOT)
stdoutがそのまま通信路なので、デバッグ用の print() を1行入れただけで規格違反になります。公式のPython SDK(mcp 2.3.0)で、ツールの中からstdoutにprintするサーバーを作って呼び出しました。
import sys
from mcp.server.mcpserver import MCPServer
mcp = MCPServer("echo")
@mcp.tool()
def echo(text: str) -> str:
"""受け取った文字列をそのまま返す"""
print("debug: echo called", file=sys.stderr) # ログはstderrへ
# print("debug: echo called") # stdoutへ書くと規格違反
return text
if __name__ == "__main__":
mcp.run("stdio")
stderrへ書いた場合はエラーなく応答が返りました。stdoutへ書いた場合、クライアント側に Invalid JSON: expected value at line 1 column 1 ... input_value='debug: echo called' という検証エラーが出ました。このSDKはその行を捨てて応答を返しましたが、規格に反した行をどう扱うかはクライアントの実装次第で、動き続ける保証はありません。ライブラリが内部で標準出力に書くことにも注意が要ります。サーバーの作り方はMCPサーバーの作り方とAIエージェントへの接続手順、MCP全体の仕組みはMCP(Model Context Protocol)とはで解説しています。
D言語のstd.stdio|writeln・readln・File型の使い方
D言語の標準ライブラリPhobosにある std.stdio は、公式ドキュメントによると、各環境のC言語のstdio.hの実装を、D言語向けの高水準なモジュールにまとめたものです。以下のコードは、2026年11月3日時点で downloads.dlang.org の LATEST が示す DMD 2.113.1 で実行しました。
出力|write・writeln・writef・writefln
出力の関数は、改行を付けるか、書式を使うかの組み合わせで4つあります。writeln は改行付きで、数値と文字列を混ぜて渡せます。
import std.stdio;
void main()
{
writeln("Hello, World!");
write("合計: ");
writeln(1 + 2, " 件");
writefln("%-8s|%5.2f", "rate", 0.125);
}
Hello, World!
合計: 3 件
rate | 0.12
書式文字列をテンプレート引数として writefln!"..." の形で渡すと、書式と引数の型をコンパイル時に検査します。%d に文字列を渡す誤りを書くと、通常の writefln("...") では実行時に FormatException で落ちますが、writefln! ではビルドが止まりました。
writefln!"%s は %d 点"("合計", "87");
// Error: static assert: "Incorrect format specifier for range: %d"
入力|readlnのEOFとreadfの先頭スペース
readln は改行文字を含んだ1行を返し、入力の終わり(EOF)では null を返します。
import std.stdio;
void main()
{
string line;
while ((line = readln()) !is null)
write(line); // lineに改行が含まれるのでwriteでよい
}
数値を読む readf は、書式の先頭に空白を1つ入れるかどうかで動きが変わります。「1・2・3」を1行ずつ入力すると、readf!" %d"(a) は3回とも読めましたが、空白なしの readf!"%d"(a) は2回目で ConvException: Unexpected '\n' になりました。前の行の改行が残っていて、空白はそれを読み飛ばす指定です。1行に複数の値が並ぶ入力は、readfln("%d %d %d", a, b, c) で1行ずつ読むと、戻り値(読めた個数)で終わりを判定できます。
ファイル|File型の自動クローズとbyLine・byLineCopy
File 型はC言語の FILE* を参照カウントで包んだ型で、同じハンドルを共有する最後のFile変数がスコープを抜けると自動で閉じます。close() を書き忘れてもファイルが開いたまま残りません。
1行ずつ読む byLine は、読み取り用のバッファを使い回すため、次の行を読むと前の行の中身が上書きされます。alpha・beta・gammaの3行のファイルを配列に集めると、結果が変わりました。
import std.array, std.stdio;
void main()
{
writeln(File("in.txt").byLine().array); // ["gamma", "gamm", "gamma"]
writeln(File("in.txt").byLineCopy().array); // ["alpha", "beta", "gamma"]
}
ループの中で1行ずつ処理して捨てるなら byLine、配列に貯める・後で参照するなら byLineCopy を使います。
例外|ErrnoExceptionとStdioExceptionは別系統
ファイルを開けない、rawRead・rawWrite が失敗した、といったOSレベルの失敗は ErrnoException、テキストの入出力の失敗は StdioException になります。2つは継承関係がないので、片方だけをcatchするともう片方は素通りします。
import std.exception : ErrnoException;
import std.stdio;
void main()
{
try
{
foreach (line; File("missing.txt").byLineCopy())
writeln(line);
}
catch (ErrnoException e)
stderr.writeln("開けません: ", e.msg);
catch (StdioException e)
stderr.writeln("読み取りに失敗: ", e.msg);
}
開けません: Cannot open file `missing.txt' in mode `rb' (No such file or directory)
D言語の標準出力もC言語と同じくバッファされるので、進捗表示では stdout.flush() を呼びます。
言語別の標準入出力|C++・Python・Go・Rust・Javaでの書き方
stdin・stdout・stderrの3本はOSが用意するものなので、どの言語にも対応する名前があります。
| 言語 | 標準入力 | 標準出力 | 標準エラー出力 |
|---|---|---|---|
| C(stdio.h) | stdin | stdout | stderr |
| C++(iostream) | std::cin | std::cout | std::cerr |
| D(std.stdio) | stdin | stdout | stderr |
| Python(sys) | sys.stdin | sys.stdout | sys.stderr |
| Go(os) | os.Stdin | os.Stdout | os.Stderr |
| Rust(std::io) | io::stdin() | io::stdout() | io::stderr() |
| Java | System.in | System.out | System.err |
C++でC言語の関数を使うときは <cstdio> を取り込みます。結果は標準出力、エラーや診断ログは標準エラー出力へ分ける設計は共通です。ただし、バッファリングの有無と書き出しのタイミングは言語・ライブラリ・設定によって異なります。
よくある質問
stdioの読み方は何ですか?
「スタンダード・アイオー」と読みます。standard input/output(標準入出力)の略で、1文字ずつ「エス・ティー・ディー・アイ・オー」と読む人もいます。ノーコードWeb制作ツールのSTUDIO(スタジオ)とは別の語です。
#include <stdio.h>とは何ですか?
printfやscanfなど、C言語の標準入出力関数の宣言が書かれたヘッダーファイルを取り込む指示です。宣言なしでprintfなどを呼ぶと、Apple clang 21.0.0の今回の検証条件や、GCC 14以降のC99以降の言語モードでは、既定でコンパイルエラーになります。
stdin・stdout・stderrの違いは何ですか?
stdinは標準入力(番号0)、stdoutは処理結果を書く標準出力(番号1)、stderrはエラーやログを書く標準エラー出力(番号2)です。stdoutとstderrはどちらも既定で画面に出ますが、リダイレクトで別々の行き先にできます。
printfの出力がすぐ表示されないのはなぜですか?
stdoutがバッファリングされているためです。パイプやファイルへ出すとフルバッファになり、改行を書いてもバッファが満ちるか終了するまで出ないことがあります。すぐ表示したい箇所で fflush(stdout) を呼びます。
MCPのstdioとは何ですか?
MCPの通信方式の1つで、クライアントがMCPサーバーを子プロセスとして起動し、標準入出力で1行1件のJSON-RPCメッセージをやり取りします。サーバーはstdoutにMCPメッセージ以外を書いてはならず、ログはstderrへ書きます。