C言語でプログラムを書くときには、どんなに短いプログラムでも必ず守らなければならない基本構造があります。
その代表が#includeとmain関数です。
本記事では、C言語初心者の方でも迷わないように、この2つの役割や書き方、よくあるエラーまでを含めて、サンプルコードと一緒に詳しく解説します。
C言語プログラムの基本構造とは
C言語の最小プログラム例と全体像
C言語のプログラムは、大きく分けて次のような構造になっています。
- 先頭に#includeなどのプリプロセッサ命令を書く部分
- その後にmain関数を書く部分
- 必要に応じてその他の関数を書く部分
まずは、ほぼ最小構成と言えるサンプルプログラムを見てみます。
#include <stdio.h> // 標準入出力ヘッダファイルを読み込む
// プログラムの実行がここから始まる
int main(void) {
printf("Hello, C language!\n"); // 画面に文字を表示する
return 0; // 正常終了を表す値を返す
}
このプログラムでは#include行とmain関数だけで、最低限の構造を満たしています。
Cコンパイラは、最終的にmain関数の中身から処理をスタートします。
そのため、どんなに複雑なプログラムでも、最終的にはこの形に集約される、と考えておくと理解しやすくなります。
「#include」と「main関数」が必ず出てくる理由
C言語の入門書やサンプルコードを見ると、ほぼ必ず先頭に#include、その後にint main(void)のような形が登場します。
これは、次のような理由によります。
#includeが必要になる主な理由は、次の通りです。
文章でまとめると、標準ライブラリ関数や定数の「宣言」を教えるためです。
printfやscanfなどの標準関数を使うために、対応するヘッダファイルを読み込む必要がある- ヘッダを読み込まずに標準関数を使うと、警告が出たり、環境によっては正しく動かないことがある
一方でmain関数が必要な理由は、とてもシンプルです。
- OSは、プログラムを起動したときに必ずmain関数から処理を開始すると決めている
- main関数が無いと、どこから処理を始めればよいか分からず、「エントリポイントが無い」というエラーになる
このように、#includeは「事前の準備」、mainは「スタート地点」と考えるとイメージしやすくなります。
#includeとは何か
#includeの役割
#includeはプリプロセッサディレクティブ(プリプロセッサ命令)と呼ばれるものの1つです。
役割はソースコードのその場所に、別ファイルの中身をそのまま貼り付けることです。
少しイメージしやすく説明すると、コンパイルの前に次のようなことが行われます。
- コンパイラの前段階で、プリプロセッサという仕組みが動く
#include <stdio.h>と書かれている場所に、stdio.hファイルの中身が丸ごとコピペされる- その結果として、コンパイラはたくさんの関数の宣言や定数の情報を知ることができる
特に標準ライブラリ関数の宣言は、ほぼすべてヘッダファイルに書かれているため、ヘッダを読み込まないと関数が正しく使えないと覚えておくと良いです。
標準ヘッダファイルとユーザー定義ヘッダファイル
ヘッダファイルには大きく2種類あります。
1つ目は、標準ヘッダファイルです。
C言語処理系(コンパイラや開発環境)が最初から用意しているヘッダで、次のようなものがあります。
stdio.h(標準入出力、printfやscanfなど)stdlib.h(一般的なユーティリティ関数、malloc、atoiなど)string.h(文字列操作、strlen、strcpyなど)
2つ目は、ユーザー定義ヘッダファイルです。
自分で作った関数の宣言をまとめたり、共通の定数定義を入れておくためのヘッダです。
標準ヘッダとユーザー定義ヘッダは、#includeの書き方が少し異なります。
- 標準ヘッダ:
#include <stdio.h>のように山かっこで囲む - ユーザー定義ヘッダ:
#include "myfunc.h"のようにダブルクォーテーションで囲む
この違いは、コンパイラがヘッダファイルを探す場所の違いに関係しています。
山かっこは標準のインクルードパス、ダブルクォーテーションはまずカレントディレクトリから探す、といったルールがあります。
#includeの基本的な書き方と書く位置
#includeを書く位置は、通常はソースファイルの先頭です。
最小の例を用いて、正しい位置を確認してみます。
#include <stdio.h> // ここはソースファイルの先頭付近に書くのが基本
// ここから先に関数などを定義する
int main(void) {
printf("Hello, include!\n");
return 0;
}
逆に、次のような書き方は避けるべきです。
- main関数の途中に
#includeを書く - 関数定義の後ろに、使う関数のヘッダを
#includeする
プリプロセッサ命令自体は、理屈の上ではソースのどこに書いても機械的には処理されますが、可読性とトラブル防止のために先頭にまとめるのが実務でも一般的なルールです。
よく使う標準ヘッダファイル例
ここで、初心者の方がよく使う標準ヘッダファイルを、役割と一緒に表にまとめます。
| ヘッダファイル名 | 主な役割・代表的な関数 |
|---|---|
stdio.h | 標準入出力。printf、scanf、puts、gets(非推奨)など |
stdlib.h | 一般的なユーティリティ。malloc、free、atoi、exitなど |
string.h | 文字列操作。strlen、strcpy、strcmpなど |
math.h | 数学関数。sin、cos、sqrtなど |
ctype.h | 文字の分類・変換。isdigit、isalpha、toupperなど |
初心者の段階では、まずstdio.hを確実に使いこなせるようになることが重要です。
画面への出力やキーボードからの入力は、ほとんどこのヘッダ経由で行われます。
main関数とは何か
main関数の役割
main関数は、C言語プログラムにおける入口(エントリポイント)です。
OSは、実行ファイルを起動したときに、必ずmain関数を探しにいき、そこから処理を開始します。
イメージとしては、次のようになります。
- 他の関数は「呼び出されなければ動かない」脇役
- main関数だけが「最初に自動的に呼び出される」特別な関数
したがって、main関数の中からprintfなどを呼び出すことで、画面に文字を出したり、他の自作関数を呼び出したりします。
なぜmain関数がないとプログラムが動かないのか
コンパイル時にmain関数が見つからない場合、多くのコンパイラは次のようなエラーを出します。
- entry point
mainnot found - undefined reference to
main
このエラーは、「どこから処理を始めればいいのか分からない」という意味です。
C言語の規格とOSの取り決めで、実行可能なプログラムはmain関数からスタートすると決まっているため、名前を変えてstart()などにすることはできません。
なお、ライブラリとして使うだけのCコード(実行ファイルを作らず、他のプログラムにリンクされるだけのコード)には、main関数が無い場合もあります。
しかし、初心者が書く一般的な「実行するプログラム」は必ずmain関数を含むと考えてください。
main関数の基本形(int main(void)など)
main関数の典型的な書き方として、次の2パターンをまず覚えておくと良いです。
#include <stdio.h>
// 引数なし、戻り値あり(int)
int main(void) {
printf("Pattern 1: int main(void)\n");
return 0;
}
#include <stdio.h>
// 引数なし、戻り値あり(int)だが、引数省略形(非推奨)
int main() {
printf("Pattern 2: int main()\n");
return 0;
}
初心者の方にはint main(void)を使うことをおすすめします。
理由としては、「引数を受け取らない」ということが明確になるためです。
int main()でも動作する環境が多いですが、昔のC言語仕様との関係もあり、厳密には「引数が未定義」という意味を持ちます。
そのため、教科書や最近の解説サイトではint main(void)を採用することが多くなっています。
戻り値(int)とreturn 0の意味
main関数の宣言はint main(...)となっています。
このintは戻り値の型を表します。
戻り値は、プログラムの終了ステータスとしてOSに返されます。
よく見るreturn 0;には、次のような意味があります。
- 0を返すことは、一般的に正常終了を意味する
- 0以外の値を返すと、エラー終了や異常終了として扱う慣習がある
簡単なサンプルで、戻り値を変えてみます。
#include <stdio.h>
// 戻り値を変更してみる例
int main(void) {
printf("プログラムを終了します。\n");
// ここで終了コードを返す
return 0; // 0なら正常終了という意味
}
このreturn 0;は、コンソール上には直接表示されませんが、シェルやバッチからプログラムを呼び出したときには、終了コードとして参照されます。
初心者の方は、まず「main関数の最後にはreturn 0を書いておく」と覚えておき、細かい終了コードの使い分けは、もう少し慣れてから学んでいくと良いです。
引数付きmain関数(int argc, char *argv[])の概要
もう1つよく出てくる形として、コマンドライン引数を受け取るmainがあります。
#include <stdio.h>
// コマンドライン引数を受け取るmain関数
int main(int argc, char *argv[]) {
printf("引数の個数: %d\n", argc);
// すべての引数を表示する
for (int i = 0; i < argc; i++) {
printf("argv[%d] = %s\n", i, argv[i]);
}
return 0;
}
このときの引数には、次のような意味があります。
argc: 引数の個数(arguments count)を表す整数argv: 引数文字列の配列(arguments vector)で、argv[0]には実行ファイル名、それ以降にユーザーが指定した引数が入る
たとえば、コマンドラインから次のように実行したとします。
./a.out apple orange
このとき、値は次のようになります。
argc == 3argv[0] == "./a.out"argv[1] == "apple"argv[2] == "orange"
初心者の段階では、コマンドライン引数を使わないint main(void)からスタートし、必要になったときにint main(int argc, char *argv[])へステップアップすると理解しやすいです。
#includeとmain関数の「お決まりパターン」とエラー対策
5分で覚えるC言語の基本テンプレート
ここまでの内容を踏まえて、初心者がまず覚えるべきテンプレートを示します。
この形を完全に暗記してしまうと、以降の学習がとても楽になります。
#include <stdio.h> // よく使う標準入出力ライブラリ
// Cプログラムの実行は必ずここから始まる
int main(void) {
// ここに処理を書いていく
printf("C言語の基本テンプレートです。\n");
return 0; // 正常終了をOSに知らせる
}
このテンプレートを基準にして、必要な#includeを増やしたり、main関数の中身を書き換えたりすることで、さまざまなプログラムを作ることができます。
上記プログラムをコンパイル・実行すると、次のような出力になります。
C言語の基本テンプレートです。
最初のうちは、プログラムを書くたびにこのテンプレートをコピペしても構いません。
慣れてくると自然に指が動くようになります。
初心者がよく起こす#includeのエラー
#includeに関するエラーは、C初心者が非常につまずきやすいポイントです。
代表的なものをいくつか挙げて、原因と対策を説明します。
ヘッダファイル名のタイプミス
たとえば、次のようなミスです。
#include <stdoi.h> // "stdio.h"と書きたかったが間違えた
この場合、コンパイラは次のようなエラーを出すことがあります。
fatal error: stdoi.h: No such file or directory
このエラーは、指定したヘッダファイルが見つからないという意味です。
スペルをよく確認し、stdio.h、stdlib.hなど正しい名前になっているかをチェックしてください。
山かっことダブルクォーテーションの混同
ユーザー定義ヘッダを読み込むときに、次のようなミスをすることがあります。
// 自作ヘッダmyfunc.hを読み込みたいが…
#include <myfunc.h> // 本来は "myfunc.h" と書くべき
この場合、コンパイラは標準のインクルードパスだけを探しに行くため、カレントディレクトリ内のmyfunc.hが見つからないことがあります。
ユーザー定義ヘッダは"myfunc.h"のようにダブルクォーテーションで囲むことを意識してください。
不要なヘッダを大量にincludeしてしまう
初心者のうちは、エラーが出ないようにとりあえず多くのヘッダをインクルードしてしまうケースもあります。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
// 実際はprintfしか使っていない…
これはエラーにはなりませんが、コンパイル時間の増加や可読性の低下につながります。
基本的には「使う機能に必要なヘッダだけをincludeする」という意識を持つと、より良いコードになります。
初心者がよく起こすmain関数のエラー
main関数に関するよくあるエラーも、いくつかパターンがあります。
main関数のスペルミス
次のような誤りは、非常によく見られます。
int mian(void) { // "main"と書きたかったが順番を間違えた
return 0;
}
この場合、コンパイラは「undefined reference to main」などのエラーを出します。
mainという名前は絶対に正しく、つねにm-a-i-nの順で書かれているか確認してください。
main関数の戻り値型が間違っている
次のようにvoid main(void)と書いてしまう例もあります。
void main(void) { // 一部の処理系では動いてしまうこともある
return; // 戻り値がない
}
古いコンパイラや一部の教材ではvoid main()を紹介している場合がありますが、C言語の標準規格では推奨されていません。
基本的には必ずint main(void)と書くようにしてください。
main関数が複数定義されている
複数のソースファイルを使うようになってくると、次のようなトラブルが起こることもあります。
main.cとtest.cの両方にint main(void)が書いてある- その状態でリンクしようとすると、「multiple definition of
main」というエラーが出る
この場合の対策は、「実行ファイルとしてビルドする対象にはmain関数は1つだけ」と覚えておくことです。
テスト用のmain関数は、一時的にコメントアウトする、別プロジェクトに分けるなどの工夫が必要です。
コンパイルエラーを防ぐチェックリスト
最後に、#includeとmain関数に関するエラーを防ぐための簡単なチェックリストを示します。
プログラムを書くたびに、次のポイントを意識してみてください。
- ソースファイルの先頭に必要な
#includeが書かれているか
たとえば、printfを使うなら#include <stdio.h>が入っているかを確認します。 - ヘッダファイル名のスペルが正しいか
stdoi.hなどになっていないか、スペルミスに注意します。 - ユーザー定義ヘッダはダブルクォーテーションで囲んでいるか
#include "myfunc.h"となっているかを確認します。 - main関数は
int main(void)またはint main(int argc, char *argv[])の形になっているかvoid main()やmianなどになっていないかを確認します。 - main関数の最後に
return 0;が書かれているか
戻り値を書き忘れると、コンパイラによっては警告や未定義動作の原因になります。
これらのポイントを習慣的に確認しておくことで、典型的なコンパイルエラーの多くを事前に防ぐことができます。
まとめ
本記事では、C言語プログラムの最も基本的な構造である#includeとmain関数について、初心者の方にも分かりやすい形で解説しました。
最初に確認したとおり、Cプログラムはおおまかに「ヘッダの読み込み」と「main関数」で構成されます。
#includeはライブラリや自作コードの宣言を取り込む準備を行い、main関数はOSから呼び出されるスタート地点として機能します。
特に初心者の段階では、次の3点をしっかり意識すると良いです。
- 標準関数を使うときは、必ず対応するヘッダを
#includeする - main関数は
int main(void)の形で定義し、最後にreturn 0;を書く - コンパイルエラーが出たときは、まず#includeとmain関数の書き方を疑う
この基本テンプレートを体に覚えさせてしまえば、その上に変数、条件分岐、繰り返し、関数分割など、さまざまなC言語の機能を少しずつ積み重ねていくことができます。
この記事で紹介したテンプレートをコピーして、実際にコンパイル・実行しながら、「いつも同じ形から書き始める」ことを習慣化していってください。
