【おさらいC言語】第1回:基礎は「ハローワールド」にギッシリあった!

全13回でお届けする【おさらいC言語】シリーズ。第1回目は、C言語の学習者であれば誰もが最初に触れたであろう「ハローワールド」について振り返っていきます。いまさらハローワールド?と思う人もいるかもしれませんが、侮るなかれ!実はそこには、C言語の基礎がギッシリと詰まっているのです。

ハローワールドとは

ハローワールドとは、画面に「Hello, world!」のようなテキストを表示するだけの小さなプログラムのことです。大抵どのプログラミング言語でも、学習書を開けば最初の例として掲載されています。はじめて作ったプログラムが「ハローワールド」だったという人も多いのではないでしょうか。見様見真似でも自分でプログラムを書いて実行し、画面に文字が表示されたときはそれなりに感動するものです。

さっそくC言語のハローワールドを振り返って見てみましょう。すると、初心者にとっては不可解な部分が多く含まれていることがわかります。ところが、学習書の最初のほうで登場することから、詳しい説明は省略されているのが普通です。「細かいことはそのうち理解できるようになるので、今は気にしないように」と言われるので、モヤモヤした気持ちを抱えたまま次のページへと進んだ人も多いでしょう。一旦納得して先へ進める人ならよいですが、それができない人は、入り口で挫折してしまってもおかしくありません。

C言語のハローワールドが入門者に難しいのはなぜか

C言語のハローワールドがなぜこんなにもモヤモヤするのかは、ほかの言語のハローワールドと比べてみればわかります。下記の例は、iPhoneなどのアプリ開発に使われるSwiftというプログラミング言語のハローワールドです。

main.swift
print("Hello, world.")

priintという機能を使い「Hello, world.」というテキストを表示しようとしている様子が一目瞭然ですね。プログラミングに詳しくなくても、ほとんどの人が納得できる内容なのではないでしょうか。

一方、C言語のハローワールドは、だいたい下記のようになっています。

main.c
#include <stdio.h>
int main(void) {
    printf("Hello, world.\n");
}

Swiftではたった1行でできることに、なんとC言語では4行も必要なのです。しかも、はじめて見る人にはなにやら意味不明なものが書かれています。ざっと考えただけでも、次のような疑問がわいてくるでしょう。

  • 最初の行はいったい何の呪文?
  • int main(void)って何?
  • printffって何?そのあとの\nは?
  • printfの頭の文字がほかの行とずれてるのはなぜ?どうやって入力すればいいの?

ただし、これらの疑問にきちんと答えようとすると、ある程度の前提知識が必要となってしまいます。そのため、学習書の最初のほうでは詳しい説明を省いて、とにかく次へ進んでもらうしか手がないのかもしれません。これが、C言語のハローワールドがモヤモヤする原因であり、初心者にとって難易度が高いプログラミング言語だといわれてしまう理由でしょう。

ワンポイント
C言語のハローワールドには呪文がいっぱい!だけど気にしちゃいけない!?

C言語のハローワールドの疑問点について解説

通常であれば「おまじないだと思って一旦スルーして」となってしまうハローワールドへの疑問について、ここではざっと解説していきます。なぜなら、これらの疑問の答えにはC言語の基礎がつまっているからです。

#includeは別のファイルを挿入する命令

C言語のプログラムは、main.cのようなテキスト形式のファイルのままでは動きません。実行するには、事前にコンパイラで実行可能(executable)形式にコンパイル(変換)する必要があります。といっても、最近ではIDE(統合開発環境)を使うことが多いので、それほど難しい操作ではないでしょう。IDEにはさまざまな種類がありますが、ほとんどの場合は「Run」ボタンを押すだけでコンパイルと実行が可能です。

IDEを使わずに、ターミナル(コマンドライン)でコンパイルと実行を行うこともできます。例えば、gccがインストールされた環境なら、次のようにすればコンパイルできるはずです。

$ gcc main.c

成功するとa.outというファイル名の実行可能ファイルができるので、次のように実行します。

$ ./a.out
Hello, world.

C言語のプログラムは上記のようにして動かすことができますが、コンパイルの際に何が行われているのかは目に見えません。しかし、ハローワールドを理解するには、コンパイルの処理内容をイメージすることが大切です。コンパイルの処理は、いくつかの段階にわかれて実行されています。その最初の段階が、「プリプロセス(preprocess)」と呼ばれる前処理です。コンパイラは、C言語のプログラムのなかに書かれた「プリプロセッサ命令(preprocessor directive)」を見つけると、それに従ってプリプロセスを行います。#からはじまる行が処理内容を指定するものです。

#include <・・・>という書き方は、「ここに・・・のファイルをインクルード(挿入)せよ」という意味の前処理です。上記のハローワールドではstdio.hというファイルの中身をこの場所に挿入せよ、という意味になります。このようなファイルは、プログラムの先頭で指定されることから「ヘッダファイル(header file)」と呼ばれ、一般的な拡張子は「.h」です。

では、そもそもなぜヘッダファイルをインクルードする必要があるのでしょうか。それは、プログラムの中で使いたい機能の「宣言」がヘッダファイルに書かれているからです。このハローワールドのプログラムmain.cの中ではprintfという機能を使いたいので、そのことを宣言するためにstdio.hをインクルードしているという訳です。このように、C言語ではプログラムの中で使う予定の機能があるとき、それに対応するヘッダファイルをインクルードしなければなりません。

なお、stdioは「標準入出力(Standard Input and Output)」という意味で、printf以外にも入出力に関するさまざまな機能が入っています。また、C言語で利用できるヘッダファイルにはstdio.h以外にもさまざまなものがあります。

ワンポイント
#ではじまる呪文は「プリプロセッサ命令」。ヘッダファイルをインクルードすれば、いろいろな機能が使えるようになる!

mainはプログラムの本体を入れる器

C言語では、プログラムを「関数(function)」の単位で記述します。関数とは、一連の処理をひとまとめにして入れておく器のようなものです。ハローワールドには、メイン関数という特別な関数が1つだけあります。メイン関数は、IDEやターミナルからプログラムを実行するときに最初に呼び出されるものです。int main(void)がメイン関数で、中身の処理を波括弧({})で囲んで記述しています。

関数の形式を示す宣言のことを「プロトタイプ」といいます。メイン関数のプロトタイプは、基本的に次の2通りです。

  • int main(void);
  • int main(int argc, char *argv[]);

関数には、「パラメータ」と「戻り値」をもたせることが可能です。パラメータは「引数」とも呼ばれ、関数の呼び出し元からどのようなデータを受け取れるかをあらわします。1つ目のプロトタイプは、ハローワールドでも使われている形式です。ハローワールドのメイン関数にはパラメータを持たせる意味がないため、voidと書かれています(voidは「空っぽ」という意味です)。2つ目のプロトタイプはパラメータを受け付ける形式です。このパラメータは、ターミナルから実行するときにオプションなどを受け付けるためのものです。これについては、次回の記事で詳しく説明します。

一方、戻り値とは関数内の処理が終了したときに呼び出し元にどのようなデータを返すかを示したものです。今回は、メイン関数の戻り値に注目しましょう。intの部分で、戻り値が整数(integer)であることが示されています。メイン関数の戻り値には、プログラムが正常に実行できたかどうかを数値で知らせる役割があります。しかし、ハローワールドのメイン関数の中では具体的な数値が指定されていません。このような場合は、正常に終了したとみなされ、それに対応する値が自動的に返されるようになっています。

なお、実際にプログラムで戻り値を指定するときは、具体的な数値を書く必要はありません。成功・失敗がわかりやすい書き方が可能です。具体的には、次の2つの値が定義されています。

  • EXIT_SUCCESS:メイン関数の実行が正常に完了した
  • EXIT_FAILURE:メイン関数の実行中に何らかの異常が発生した

これらの値はstdlib.hで定義されているので、使うには#includeが必要です。例えば、正常終了を示す値をメイン関数から返すには、returnを使って次のように書きます。

#include <stdio.h>
#include <stdlib.h>

int main(void) {
    printf("Hello, world.\n");
    
    return EXIT_SUCCESS;
}

returnがないハローワールドでは、メイン関数の戻り値が省略されていたため常に正常に実行されるものとみなされていました。これは、上記のように戻り値を明記したプログラムがEXIT_SUCCESSを返すのと同じ動作です。

ワンポイント
ハローワールドのメイン関数にはパラメータも戻り値の指定もない!

printfはテキストを出力してくれる関数

関数には、メイン関数のように自分で作らなければならないものもあれば、プログラムの中から呼び出して使えるものもあります。関数を呼び出すには、関数名のあとに括弧(())を書くのがC言語の決まりです。パラメータは括弧の内側に書き、最後に命令の終わりを示すセミコロン(;)をつけます。

printfは、書式にしたがって画面にテキストを出力してくれる関数です。「印刷」を意味する「print」に「書式(format)」の頭文字「f」を組み合わせた関数名になっています(画面に出力するなら「display」じゃないのか?という気もしますが、文字を表示するときには「print」という言葉がよく使われます)。ハローワールドでは、通常はprintf関数で指定する書式が省略されているので少し奇妙に見えるかもしれません。また、printfはテキストを出力したあとに改行してくれません。そのため、改行コード(\n)を明記して改行させる必要があります。

ワンポイント
省略できることとお約束が混ざっているから理解が難しい!

読みやすくするために空白を入れるのは自由

C言語では、単語の切れ目など空白文字を入れなければならない箇所が決まっています。空白は続けて何文字入れても構いません。半角スペースだけでなく、タブや改行も空白とみなされます。ただし、プリプロセッサ命令だけは1行に1つでなければなりません。

プログラムを読みやすくするために、行の頭に空白を入れるのはプログラマの自由です。一般的に、波括弧で囲まれた行の頭は下げるようにします。行の頭を下げることを「インデント」や「字下げ」などといいます。インデントの幅は「半角スペース4つ」や「タブ1つ」のようにルールを決めておくとよいでしょう。反対に、改行やインデントを減らすこともプログラマの自由です。読みづらくなってしまいますが、試しにハローワールドを詰めて書くと下記のようになります。

#include <stdio.h>
int main(void){printf("Hello, world.\n");}
ワンポイント
プログラムで行う処理がわかりやすいように、ルールを工夫しよう!

C言語の2種類のコメント

ここまで、ハローワールドへの疑問についてざっと解説してきました。なんとなくモヤモヤしていたところが解消したのではないでしょうか。最後に、ハローワールドには出てきませんでしたがC言語でのコメントの書き方について解説しておきます。

ハローワールドは規模が小さいので問題ありませんが、プログラムが大きくなってくると読み返したときにどの部分がどんな処理をしているのかがわからなくなってしまいます。そのため、プログラムの内容などを説明する「コメント」をつけるのはよい習慣です。コメントはプリプロセスの段階で取り除かれるので、何を書いてもコンパイル結果に影響を与えません。

C言語のコメントには、2種類の書き方があります。下記の例のように/**/で囲むか、//に続けて書きます。

/* これはハローワールドのプログラム
(この形式のコメントでは、途中に改行を入れられます) */

#include <stdio.h>
int main(void) {
    printf("Hello, world.\n"); // これは関数呼び出し(行の終わりまでがコメントです)
}

/**/で囲む形式はC言語の初期バージョンから利用できる書き方で、1つのコメントが複数行にわたっても構いません。一方、//に続けて書くのはC++から取り入れられた比較的新しい形式で、その行の終わりまでがコメントになります。

ワンポイント
プログラムの中にコメントを入れることで、わかりやすくしよう!

まとめ

C言語のハローワールドは、たった数行からなる小さなプログラムです。しかし、そこにはC言語の基礎である決まりごとや工夫が凝縮されています。いままでわかったつもりで見過ごしていた人も、振り返ってみると新たな発見があるかもしれません。ハローワールドのプログラムを隅々まで観察すると、まだまだ説明しきれていない部分も残っています。より理解を深めていくためには、プログラムがコンピュータの内側でどのように動作するのかについて、イメージしてみることが大切です。