C言語基礎|再帰関数呼出し

「1文字ずつ読んで、1文字ずつ出す。たったそれだけで入出力の“基本形”が見えてくる」

文字の入出力は、プログラムの会話そのもの

多くのプログラムは、キーボードやファイル、通信などから文字を読み込み、何かの形で文字を出力しています。
その“最小単位”が 1文字の入出力 です。

C言語には、1文字を読む getchar と、1文字を出す putchar があります。
この2つを組み合わせると「入力をそのまま出力する(コピーする)」という、入出力の基本形を作れます。

この記事では、元の「入力を標準出力へコピー」例を、少し味付けして 入力を大文字にして出力する シンプルな例に置き換えます(表示文も別の日本語に変更)。
その上で、EOF、while の条件式、代入と判定が一体になった書き方の意味を、表と図で丁寧に追いますね。

サンプルプログラム

例:入力を大文字にして出力する(EOF で終了)

※英字以外はそのまま出力します。

プロジェクト名:chap8-10-1 ソースファイル名:chap8-10-1.c

#include <stdio.h>

int main(void)
{
    int ch;

    puts("文字を入力してください(終了は Ctrl+D または Ctrl+Z)。");

    while ((ch = getchar()) != EOF) {
        if ('a' <= ch && ch <= 'z')
            ch = ch - ('a' - 'A');   // 小文字→大文字
        putchar(ch);
    }

    return 0;
}

実行例(イメージ)

文字を入力してください(終了は Ctrl+D または Ctrl+Z)。
Hello!
HELLO!
This is a pen.
THIS IS A PEN.

getchar と putchar:1文字入出力の基本ペア

getchar / putchar の役割

関数役割入力/出力返すもの
getchar1文字読む標準入力から入力読んだ文字(int)または EOF
putchar1文字出す標準出力へ表示出力した文字(int)または EOF

表の説明

  • getchar は「次の1文字」を取り出して返します。
  • putchar は「指定した1文字」を表示します。
  • どちらも戻り値は int です(ここが重要で、EOF を扱うためです)

書式:getchar / putchar はこう使う

getchar の書式

int getchar(void);

何をする?
標準入力から次の1文字を読み込み、その文字を返します。読み込めないときは EOF を返します。

putchar の書式

int putchar(int c);

何をする?
c で指定された1文字を標準出力へ書き込みます。失敗すると EOF を返すことがあります。

EOF とは何か:文字が尽きた合図(またはエラー)

EOF は End Of File の略で、「もう読み込む文字がない」または「読み込みエラー」を表す特別な値です。

EOF のイメージ

入力が続く  → getchar は文字コードを返す(例:'H')
入力が終わる→ getchar は EOF を返す

EOF のポイント

項目内容
EOF の正体<stdio.h> で定義されたマクロ
多くの処理系で負の値(例:-1)
使いどころgetchar の終了判定、ファイル読み込みの終了判定
注意char ではなく int で受けるのが安全

表の説明
EOF は「文字ではない値」なので、char 型に入れると判定に失敗する可能性があります。
だから int ch; が定番です。

重要:なぜ int ch; なの?

「1文字だから char でいいのでは?」となりがちですが、EOF を確実に扱うには int が安心です。

char ではなく int を使う理由

受け取り型EOF を安全に判定できる?理由
char怪しいことがあるchar が符号なしだと EOF(負数)が表せない場合がある。
int安全EOF も通常の文字コードも両方表せる。

while の条件式が少し複雑に見える理由

この1行がこの記事のキモです。

while ((ch = getchar()) != EOF)

「代入」と「EOF 判定」を一体化しています。

制御式の分解(読み方)

(1) ch = getchar()      まず1文字読む(結果を ch に代入)
(2) ch != EOF           その結果が EOF かどうか調べる
(3) EOF でなければループ継続

説明

  • 代入式 ch = getchar() は「代入した後の値」そのものが式の値になります
  • その値を != EOF で比較して、続けるか止めるか決めています

代入と判定を分けて書くとどうなる?

同じ処理を、もっと素直に書くとこうなります。

while (1) {
    ch = getchar();
    if (ch == EOF)
        break;

    if ('a' <= ch && ch <= 'z')
        ch = ch - ('a' - 'A');

    putchar(ch);
}

一体型と分離型の比較

書き方良いところ注意点
while ((ch = getchar()) != EOF)短く書ける、入出力の定番形初見だと読みにくい
代入→if→break読みやすい、デバッグしやすい少し長くなる

表の説明
慣れるまでは分離型で理解して、意味が分かったら一体型に戻すのがおすすめです。

小文字→大文字の変換が何をしているか

今回の例では、英字だけ大文字化しています。

if ('a' <= ch && ch <= 'z')
    ch = ch - ('a' - 'A');

変換のイメージ(ASCII 前提)

'a' と 'A' の差だけ引くと小文字→大文字になる
例: 'c' → 'C'

図の説明

  • ASCII では 'a'〜'z' と 'A'〜'Z' が一定の差で並んでいます
  • その差 'a' - 'A' を使って変換しています
    (より安全にやるなら ctype.h の toupper もありますが、今回は仕組みが見えやすい形にしています)

入力の終わらせ方(環境メモ)

  • Windows のコンソールでは Ctrl+Z を入力して Enter のことが多いです
  • UNIX/Linux/macOS の端末では Ctrl+D が一般的です

環境によって少し違いますが、「EOF を送る操作」だと思えばOKです。

登場する命令・構文の役割まとめ

while の書式

while (条件) {
    文;
}

何をする?
条件が真の間、繰り返します。EOF が来るまで読み続ける定番の形に使われます。

if の書式

if (条件)
    文;

何をする?
条件に当てはまるときだけ処理します。今回だと小文字判定に使っています。

break の書式

break;

何をする?
ループを強制終了します。EOF を分離型で書くときに使います。

まとめ:文字入出力の基本形を押さえると強い

  • getchar は 1文字を読み、読めなければ EOF を返す。
  • putchar は 1文字を出力する。
  • ch は int で受けるのが安全(EOF を扱うため)
  • while ((ch = getchar()) != EOF) は「読む→EOF 判定→繰り返す」の定番形
  • 文字処理は「読みながら加工して出す」が基本パターン