Cプログラミング入門

第6回Chapter 2 Cプログラミング最初の一歩~Hello Worldからはじめよう(4)

セミコロンは重要

printf()やretrnの右側には、セミコロン(;)を付ける必要があります。

printf()自体は関数(関数呼び出しを行う式)ですが、これにセミコロン付けることによって、ひとつの(ステートメント)となります。returnは制御文のひとつですが、同様にセミコロン付けることによってひとつの文となります。このように、C言語のプログラムは、基本的に文の並びとして記述されます。

図2.9  セミコロンを付けると文になる
  printf()  ← 関数呼び出しを行う式
  return    ← 制御文

  printf(); ← 文(式文)
  return;   ← 文(return文)

C言語のプログラムでは、セミコロンを付け忘れたためにコンパイル時にエラーが発生したということがしばしば起こります。C言語のプログラムは、後述のとおりフリーフォーマットとなっていて行単位の概念がないため、たとえばprintf()のあとのセミコロンが抜けていると、Cコンパイラはさらに次の行まで何らかの式が続いているものと解釈してコンパイルを行おうとしてしまいます。

セミコロンを忘れた場合のコンパイラのエラーメッセージは、セミコロンとはかけ離れた不可解なメッセージになるのが普通です。そのような場合は、エラーが出た行よりも前の行に、セミコロンを付け忘れている文がないかどうかチェックするとよいでしょう。

図2.10の実行例は、hello.cの中のprintf()の右のセミコロンを付け忘れた場合のエラーメッセージです。このように、エラーはprintf()の行ではなく、次の行のreturnの直前で構文解析上のエラーとして表示されます。

図2.10 セミコロン(;)を付け忘れたhello.cのコンパイルエラー
$ gcc hello.c
hello.c: In function `main':
hello.c:7: parse error before `return'
$

複文の{ }について

複数の文を{ }で囲ってひとつの複文(ブロック)とすることができます。この複文は、後述のif文やfor文などの中でおもに使用されます。複文はそれ自体が文なので、{ }のあとにさらにセミコロンを付ける必要はありません。

「#include」とは

hello.c(第3回のリスト2.1参照)の1行目に書いてある#include <stdio.h>というのは、printf()などの関数の定義が書かれたstdio.hというヘッダファイルを読み込むという意味です。プログラム中でprintf()を使うには、あらかじめ「#include <stdio.h>」と書いてprintf()関数を定義する必要があります。なお、ヘッダファイルは、stdio.hのように拡張子を「.h」にします。

この「#include」は、実は後述のプリプロセッサに対する指令です。プリプロセッサの指令は、必ず行の左端に「#」を書く必要があります。hello.cのように1行目に「#include <stdio.h>」と書くと、この位置にstdio.hという別のファイルが読み込まれます。コンパイラはstdio.hが読み込まれた状態のhello.cをコンパイルします。つまり、hello.cをコンパイルすると、実はhello.cだけでなく、stdio.hというファイルも含めてコンパイルしていたのです。

stdio.hは、システムによってあらかじめ用意されたヘッダファイルのひとつです。本来、printf()という関数を使うには、まずその関数を定義しなければなりません。しかし、その関数の定義を毎回書いていたのでは不便です。そこで、システム側ですでにprintf()などの関数の定義が書かれているstdio.hというファイルを用意しておき、プログラマ側では単に「#include <stdio.h>」と書くだけでよいという仕組みになっているのです。

stdio.hは、実際には/usr/include/stdio.hという場所に存在します。⁠#include <stdio.h>」のようにファイル名を< >で囲んで書くと、/usr/includeというディレクトリからファイルが読み込まれるのです。実際に/usr/includeディレクトリをlsコマンドで見てみると、そこにはstdio.hだけでなく多数のヘッダファイルがあることがわかります。

自分で作れるヘッダファイル

ヘッダファイルは自分で書いて用意することもできます。自分が書いたヘッダファイルを読み込ませるには、

#include "my_header.h"

のように、ファイル名をではなく" "で囲みます。" "で囲んだ場合は、ヘッダファイルはカレントディレクトリから読み込まれ、この場合はカレントディレクトリにあるmy_header.hというファイルが読み込まれることになります。

#include <stdio.h>       ←   /usr/include/stdio.hが読み込まれる
#include "my_header.h"   ←   ./my_header.hが読み込まれる

文字列とは

hello.cの"Hello World\n"のように、ダブルクォーテーション(")で囲まれた文字の集まりのことを文字列といいます。文字列の後ろに付いている\n改行コードを表すC言語の拡張表記です。つまり、printf()による文字列の表示の際には、画面上で直接見える「Hello World」の文字列の直後に、改行コードが出力されているのです。

この改行コードは、メッセージの表示の際には通常必要です。改行コードがないと、実行終了後のシェルのプロンプトの位置がおかしくなったり、また、出力のバッファの関係で意図したタイミングで文字列が表示されないことがあります。

バックスラッシュ(\)を使った文字コードの拡張表記には、改行コードの\nのほかに、いくつかの表記があります。詳しくはアスキーコード表を参照してください。これらの拡張表記は、コントロールコードのように、画面に直接表示できない文字を表記するために使用されます。さらに、" "で囲まれた文字列中で「"」を使いたい場合は\"と表記し、\自体は\\と表記します。

なお、バックスラッシュ(\)はOSや環境設定によっては(\)に見えますが、文字コード自体は同じであり、単に表示上の問題であるため、どちらも同じものと考えてください。

表2.1 バックスラッシュ(\)を使ったおもな拡張表記
表記実際の文字コード
\n改行コード
\""
\\\

ソースファイルはフリーフォーマット

C言語のソースファイルには、基本的には行の概念がありません。これは、アセンブラなどの行単位で記述されたソースファイルとは異なるところです。そもそも行というのは、改行コードで区切られた文字列ですが、C言語の文法では、改行コードだけでなく、スペースもTABも、みな同じように、単なる区切りのための文字として扱われます。つまり、改行の代わりにスペースを使って1行につなげても、逆にスペースだったところで改行して2行に分けても、C言語のプログラムとしてはまったく等価なのです。

hello.cの改行位置などを変えて記述した奇妙なソースファイルの例を、リスト2.5(hello_strange_1.c)およびリスト2.6(hello_strange_2.c)に示します。これらはどちらも、もとのhello.cとC言語の文法的に等価です。

リスト2.5 hello_strange_1.c
#include <stdio.h>
int main(){printf("Hello World\n");return 0;}
リスト2.6 hello_strange_2.c
#include <stdio.h>
             int
            main
           (
          )
         {
        printf
       (
      "Hello World\n"
     )
    ;
   return
  0
 ;
}

このようにC言語のソースファイルは、プログラマが任意に改行・スペース・TABを入れてフリーのフォーマットで記述することができますが、一般的にはC言語の構文がわかりやすいようにインデント(字下げ)を行うなどの習慣があります。具体的には関数内や、次回以降に扱うif、for、whileなどの構文内では、行頭に複数のスペースまたはTABを入れてインデントします。ただし、具体的なソースのスタイルには、プログラマの好みの問題もあり、絶対的な基準はありません。

なお、#includeなどのプリプロセッサのための行はフリーフォーマットではなく、⁠#」は行の左端に書いて行の概念を持って記述しなければなりません。

#defineを使ってみる

「#include」のほかに「#define」というプリプロセッサの指令もあります。⁠#define」を使えば、任意のマクロを定義して、プログラム中の文字列を置き換えることができます。たとえば、

#define MESSAGE "Hello World\n"

と定義しておけば、プログラム中にMESSAGEというマクロ名を書くと、それはコンパイル前に"Hello World\n"と展開されます。

hello.cを、⁠#define」を使って書いた例をリスト2.7に示します。このように書いても、もとのhello.cとまったく同じプログラムとして動作します。

リスト2.7 hello_define.c
#include <stdio.h>

#define MESSAGE "Hello World\n"

int
main()
{
  printf(MESSAGE);
  return 0;
}

「#define」による文字列の置き換えは、C言語の文法とは関係なく行なわれます。⁠#define」は、定数値をマクロとして定義したり、マクロ関数を定義したりするのに使われます。なお、定数値を「#define」で定義する場合は、マクロ名を習慣的に大文字とします。

おすすめ記事

記事・ニュース一覧