![見出し画像](https://assets.st-note.com/production/uploads/images/110193452/rectangle_large_type_2_27ec238089e008b30b12f4d6cc6fdbf2.png?width=1200)
C言語教室 第32回 プロトタイプ宣言とインクルードガード
さて、前回の教室で分割されたソースファイルで、そのファイルに含まれていない名前の宣言が必要だという話をしましたが、今回もその続きです。
C言語教室 第1回 - 関数呼び出し
で、インクルードファイル云々以前に関数を書く順序を変えた時に
extern void ave();
というオマジナイが必要になるという話をしましたが、別に「他のファイル」ではなく同じファイルの中でも定義よりも先に使うのであれば extern を書くことで解決することができます。これは2つの関数が相互に互いを呼ぶ場合には必ず起こりますし、書く順序を気にしないために、定義を書く”.c”ファイルにおいても、”.h”ファイルをインクルードすれば必要な宣言は済んでいる状態で書き始めることができるわけです。
これを前回、説明した”ave.c”を例にすると
[ave.h]
extern void ave();
extern int a;
extern int b;
[ave.c]
#include <stdio.h>
#include “ave.h”
int a;
int b;
void ave() {
int v;
v = (a + b) / 2;
printf("Ave(%d,%d)=%d\n", a, b, v);
}
となります。
古いC言語では、これで間に合わせていたのですが、そもそも extern で指定するのは、関数名とその戻り値だけで、引き数の数と型についてはチェックしていません。だからこそ printf では表示したい内容に応じて1つ以上の引き数をいくらでも追加することが出来たのですが、呼び出す側と呼ばれる側で引き数の数はもちろん型が違っても構わないままなのは、エラーの温床なので、これを何とかチェックしたいということで、プロトタイプ宣言という仕組みが出来ました。
C言語 プロトタイプ宣言の効果【関数を安全に呼び出す仕組み】
プロトタイプ宣言では、戻り値、関数名に続いて引き数については、その型だけを書きます。中身は書かないので仮引き数名と呼ばれる名前は不要です。そしていきなり”;”で終わります。互換性のために引き数を書かない場合は「引き数のチェックをしない」という意味になるので、引き数が無いのであれば、そこに”void”と書きます。先の ave関数であればプロトタイプ宣言は
void ave(void);
と書きます。これを踏まえて先の”ave.h”を直してみましょう。
[ave.h]
void ave(void);
extern int a;
extern int b;
これで”ave.h”さえインクルードすれば、ave関数の正しい引き数の型と戻り値の型がわかるので、正しく呼び出されていることをチェックすることが出来るようになり、もし合っていなければコンパイル時に教えてくれます。
さてプロトタイプ宣言の引き数で型を正確に宣言するようになったことで、ここで登場する構造体・共用体の名前の宣言を、この宣言より先に行う必要が出てきます。そこで構造体の宣言をヘッダファイルの中に書くのですが、同じ構造体を使う関数がたくさんあると構造体の宣言だけのヘッダファイルを作って、これをヘッダファイルでインクルードするようになります。
ここで何かが心配になった人は察しが良いです。同じ構造体の宣言は一度しか出来ず、ニ度宣言するとエラーになります。他にも値を名前で定義する #define でも同じことが起きます。
これを回避するためにインクルードガードと呼ばれる手法が使われるようになりました。”ave.h”では構造体の宣言もマクロの定義もありませんが、これを例にすると
[ave.h]
#ifndef AVE_H
#define AVE_H
void ave(void);
extern int a;
extern int b;
#endif /* AVE_H */
と、ヘッダファイル全体を#ifndef で囲みます。こうすることで1度展開された”ave.h”は、その中で”AVE_H”が定義され、2度めからは #ifndef が成立しなくなるので中身が空になります。少しばかり面倒ですが、これをすることでインクルードの順序で悩まされなくなり、どんどんインクルードしても大丈夫になります。
C言語 インクルードガードとは? #pragma once
【C】初めてのC言語(11. ヘッダファイル)
最近の処理系では、プリプロセッサ命令として #pragma once が追加されていて、これが使えるのであれば、先の例は
[ave.h]
#pragma once
void ave(void);
extern int a;
extern int b;
と書くことが出来、同じヘッダファイル名で事故を起こすことも防げます(どういう事故が起こるかはもうわかりますよね)。
C言語のインクルードガードはpragma onceを使う
課題を楽しみの方には申し訳ありませんが、今回も課題はありません。もう少しお待ち下さい。
次回は、2つの static を説明します。
ヘッダ画像は、いらすとや さんよりhttps://www.irasutoya.com/2018/06/blog-post_80.html
いいなと思ったら応援しよう!
![kzn](https://assets.st-note.com/production/uploads/images/111925158/profile_dffba6e25229ee04f46397fb91bbfc20.jpg?width=600&crop=1:1,smart)