第15回
関数の宣言~ライブラリとヘッダファイル

プロトタイプ宣言の省略

変数は使う前に必ず宣言しなければなりませんが、関数ではそれを省略できる場合があります。これをうまく利用すると、ソースの記述を簡略化できます。

関数宣言の例外

関数のプロトタイプ宣言は基本的に必須なのですが、1つだけ例外となるケースがあります。それは、『関数の呼び出しより先に定義を記述』した場合です。なぜなら、Cには
関数の定義は宣言を兼ねる
というルールがあるからです。そのため、関数を呼び出すコードの前にその関数の定義を記述しておけば、別途宣言する必要はなくなります。

リスト1は『入力した文字列を縦に表示する』プログラムです。本コラムの第9回で「リスト5」として紹介したものと、動作は同じです。ただ、文字列を縦に表示する部分を、"tateputs"という別の関数として定義し、main関数から呼び出すようにしています。

関数tateputsはchar *(char型のポインタ)を引数とし、それの示す文字列から1文字ずつ取り出しては標準出力へ送り出す──という動作をします。

リスト1:入力された文字列を縦に表示するプログラム~ごく一般的な書き方(ex1501.cex1501.exe)
#include <stdio.h>
#include <string.h>

int tateputs(char *); /* 縦表示関数のプロトタイプ宣言 */

int main(void)
{
  char str[256 + 1];  /* 入力された文字列を保存 */
  int retcode;        /* シェルへの終了コード */

  printf("Input String : ");
  scanf("%s", str);

  retcode = tateputs(str);  /* 縦表示関数を呼び出す */
  return (retcode);
}

/* tateputs -- 文字列を縦に表示する */
int tateputs(char *s)
{
  if (strlen(s) <= 0)
    return (-1);  /* 引数の文字列が空ならエラーで戻る */

  while (*s) {             /* 文字がNULL(0)でない間繰り返す */
    printf("%c\n", *s++);  /* 1文字に改行を付け足して出力 */
  }
  return (0);
}

mainより前に定義する

リスト3行目の「int tateputs(char *);」がtateputs関数のプロトタイプ宣言です。これを省略すると、コンパイル時に「tateputsは宣言されていない」というエラーが発生します。

このように、
ソース中で使用する独自の関数を宣言
main関数を定義
独自関数を定義
……という形が、ごく一般的なCのソースの書き方です。

ここで、ソースをリスト2のように書き直してみます。

リスト2では、main関数から呼び出されるtateputs関数の定義を先に記述しています。こうすることで、リスト1にあった「int tateputs(char *);」というtateputs関数のプロトタイプ宣言が省略できます。

リスト2:入力された文字列を縦に表示するプログラム~独自の関数を先に定義(ex1502.cex1502.exe)
#include <stdio.h>
#include <string.h>

/* tateputs -- 文字列を縦に表示する */
int tateputs(char *s)
{
  if (strlen(s) <= 0)
    return (-1);  /* 引数の文字列が空ならエラーで戻る */

  while (*s) {             /* 文字がNULL(0)でない間繰り返す */
    printf("%c\n", *s++);  /* 1文字に改行を付け足して出力 */
  }
  return (0);
}

int main(void)
{
  char str[256 + 1];  /* 入力された文字列を保存 */
  int retcode;        /* シェルへの終了コード */

  printf("Input String : ");
  scanf("%s", str);

  retcode = tateputs(str);  /* 縦表示関数を呼び出す */
  return (retcode);
}

小さなツールの開発に便利

リスト2のような書き方は、厳密に言えばC本来の『正しい書き方』ではありません。まず関数の宣言をしてからその関数を呼び出すmain関数を定義し、その後に呼び出される独自の関数をまとめて定義するのが正攻法です。

しかし、ソースファイルが1つで済ませられる小さなプログラムでは、リスト2のような「本末転倒式」の書き方もよく用いられます。

ちょっとした文字列処理用ツールを作るときなど、(本当はいけないことなのですが)慣れてくると仕様書や関数分担表、フローチャートなどの設計図を一切書かず、いきなりソースを書き始めることがあります。そんな場合、独自に作成する関数の名前を適当につけておいて、うまく動くことが確認できてから名前を《それらしいもの》に書き直したりします(^^ゞ

ミスを見つけてソースを修正するとき、リスト1のような正攻法では「プロトタイプ宣言・関数の呼び出し・関数の定義」の3箇所を修正しなければなりませんが、リスト2のような形にしておけば「関数の定義・関数の呼び出し」の2箇所の修正で済みます。要は手抜きテクの一種なのですが、ちょっとした処理を書くときには便利です。

ただし、複数のソースファイルを使って規模の大きな処理を記述する場合は、事前の設計はもちろん、別途ヘッダファイルを用意して、その中に各ソースファイルで使用する関数のプロトタイプ宣言を記述しなければなりません。複雑なソースでリスト2のような書き方をしてプロトタイプ宣言を省略すると、訳が分からなくなる場合があるので注意しましょう。