標準入出力(1)

このエントリーをはてなブックマークに追加

標準入出力はC言語さえ出来れば誰でも知っている簡単な仕組みですが、Unix系OSでは非常に深く関わっている仕組みですので一つ一つ説明していきます。ここでははC言語の標準関数を使用して説明していきます。

標準入出力とは

標準入出力とは"通常"、キーボードからの入力やターミナルへの出力のことです(まあ、C言語の規格ではどこから入力したり、どこへ出力するかも決まっていませんが...)。WidowsではGUIが当たり前でほとんど使用しませんが、Unixではこれらの機能例えばlsコマンドを実行した結果は標準出力を使用しています。sortやcut等など様々なコマンドが利用する大切な概念です。


標準入出力は3種類

標準入出力には3種類あります。

名前説明
標準入力stdin
標準出力stdout
標準エラーstderr


まずは基本のHello Worldから

まずはHello WoldとコンパイルのHello Worldを元にこれらを解説していきますので、もう一度見てみます。

//helloworld.c
#include <stdio.h>

int main(void)
{
	printf("Hello Wold\n");

	return 0;
}

コンパイルと実行をします。とくに問題ありません。
$ gcc -o helloworld helloworld.c
$ ./helloworld
Hello World


標準出力と標準エラー

標準出力

さて、このHello Worldをstdoutを使うように書き換えます。標準入出力を明示的に指定するにはfprintf(3)を使用します。

//helloworld_stdout.c
#include <stdio.h>

int main(void)
{
	fprintf(stdout, "Hello Wold\n");

	return 0;
}

コンパイルと実行をします。実行結果はprintf(3)版のHello Worldと同じはずです。
$ gcc -o helloworld_stdout helloworld_stdout.c
$ ./helloworld_stdout
Hello World


標準エラー

次は標準エラーです。

//helloworld_stderr.c
#include <stdio.h>

int main(void)
{
	fprintf(stderr, "Hello Wold\n");

	return 0;
}

コンパイルと実行をします。
$ gcc -o helloworld_stderr helloworld_stderr.c
$ ./helloworld_stderr
Hello World

実行結果はstdout版と違いが無いように見えますが実は違うのです。


出力とShell

これを確かめる為に次のように実行してみてください。

$ ./helloworld_stdout < result_stdout
$ ./helloworld_stderror < result_stderror
Hello World
$ cat result_stdout
Hello World
$ cat result_stderror
$

人間の目で見ると違いは分かりませんがこのようにすると一目瞭然です。stdoutは通常の出力をしますが、stderrはエラー専用の出力へと出力します。shellはこれらを理解します。 ">"は標準出力が対象になります。詳しくはUnixとC言語の連携を見てください。


標準入力

まずは教科書どおりにscanf(3)で

次は標準入力です。とりあえず、入門なのでscanf(3)を使います。scanf(3)は多々問題がありますが、例題なので構わないでしょう。

//input.c
#include <stdio.h>

int main(void)
{
  int x;

  scanf("%d", &x);
  fprintf(stdout, "get:%d\n", x);

  return 0;
}

コンパイルと実行をしてみます。教科書どおりの動作ですね。
$ gcc -o input input.c
$ ./stdin
1
get:1


stdinを使用してみよう

さて、stdout、stderrと同じくstdinを使用するように変換します。printf(3)をfprintf(3)にしたようにstdinの場合はscanf(3)をfscanf(3)にします。

//input_stdin.c
#include <stdio.h>

int main(void)
{
  int x;

  fscanf(stdin, "%d", &x);
  fprintf(stdout, "get:%d\n", x);

  return 0;
}

コンパイルと実行をしてみます。input.cと同じ動作になる事を確認してください。
$ gcc -o input_stdin input_stdin.c
$ ./stdin
1
get:1


関数と、標準入出力の関係

関数の名付けルール

printf(3)をfprintf(3)に、scanf(3)をfscanf(3)にしたように入出力を指定するには頭に"f"を付けます。
例えば、getc(3)にはfgetc(3)にといった具合です。


標準入出力ってそもそも何?

そもそも標準入出力とは何なのでしょうか? まずはfprintf(3)のプロトタイプを見てみましょう。

#include <stdio.h>
int fprintf(FILE *stream, const char *format, ...);

stdin等はFILE*型のようです。つまり単なるポインタです。ファイル入出力ではfopen(3)の戻り値をfprintf(3)に渡して使いますが、標準入出力はファイルと何ら代わりが無いのです。
つまり、入出力先がファイルか標準入出力かだけです。UNIXの特徴の1つがファイル、キーボード、ターミナルをファイルという1つの概念に抽象化を行った点です。
これにより、プログラマはファイルやキーボードを必要以上意識しなくてもよく、プログラムもfprintf(3)の第一引数を変更するだけという非常に簡単なプログラムがかけるのです。


まとめ

次はこのページで作ったプログラムで使用した標準関数をシステムコールに置き換えて行きます。これがUnix系OSを扱う為の第一歩となります。