C応用6 モジュール分割・記憶クラスと通用範囲
C応用6 モジュール分割・記憶クラスと通用範囲
プログラムは、複数のソースファイルに分けて書きます。これをモジュール分割と言います。
モジュール分割する時、他のソースファイルで宣言したものを別のソースファイルでも使いたい場合が出来てきます。構造体タグ宣言は、その一つになりますが、このような宣言は、ソースファイルではなく、ヘッダファイルと呼ばれる別のファイルに作成します。ソースファイルは「#include文」を使って、ヘッダファイルを取り込むようにします。ヘッダファイルを作成する時は、#include防御と呼ばれるプログラムの書き方をしなければなりません。
今までは、複数の関数で共有する変数は、グローバル変数(関数の外で宣言した変数)にするだけで良かったのですが、モジュール分割する場合は、記憶クラスと通用範囲を考えた変数宣言をしなければなりません。
本章では、モジュール分割で必要となる知識とテクニックについて説明します。
プログラムは、複数のソースファイルに分けて書く事ができます。モジュール分割は、関数定義単位に分けますが、関数定義以外の所は、基本的には、ヘッダファイルと呼ばれるファイルに分けて書きます。
プログラムを、複数のファイルに分けて書くのに必要な文法を説明しています。
・プログラムは関数定義単位にファイルに分けて書ける
一つの関数定義を、ファイルに分けて書く事はできませんが、複数の関数定義を一つにファイルに書くことはできます。
・関数定義の外に書くものはヘッダファイルに書く
自分で作るヘッダファイルを私用ヘッダと呼びます。
これに対して、コンパイラが用意しているヘッダファイルを標準ヘッダと呼びます。
・私用ヘッダは " " を使って取り込む
私用ヘッダと標準ヘッダでは、#include文の書き方が違います。
・グローバル変数はヘッダファイルにextern宣言する
グローバル変数宣言は、ヘッダファイには書きません。グローバル変数宣言に対応したextern宣言をヘッダファイルに書きます。
extern宣言は、グローバル変数と同じ書き方をしますが、最初にexternというキーワードを付け、初期化式は書きません。要素数を省略した初期化付きの配列宣言の場合は、初期化式がないので、サイズ不定配列になります。サイズ不定配列は、sizeof演算子で配列サイズを求められません。
5個以下の整数をキー入力して入力した整数を表示するプログラムが、YouTubeの動画説明に記載のURLからダウンロードできます。main.cpp, input_array.cpp, print_array.cppの3つのソースファイルに分割しします。また、各.cppファイルに対して、.hファイルを作成し、必要とする.hファイルをインクルードするようにします。
練習問題41と同様に5個以下の整数をキー入力して、入力した整数を表示するプログラムが、YouTubeの動画説明に記載のURLからダウンロードできます。今回のプログラムは、配列宣言と入力した個数を記録する変数をグローバル宣言し、入力関数と表示関数は、これらのグルーバル変数を使うようにしています。前回と同様に、main.cpp, input_array.cpp, print_array.cppの3つのソースファイルに分割し、各.cppファイルに対して、.hファイルを作成して、必要とする.hファイルをインクルードするようにします。
今回は、グローバル変数宣言に対応するextern宣言を行います。
プリプロセッサ文には、マクロ名を使った条件を判定できるものがあります。これが、#if系のプリプロセッサ文です。コンパイラが用意する標準ヘッダの中でよく使われるプリプロセッサ文ですが、普通のプログラムを作る時は、#include防御で使われる位です。#include防御は、ヘッダファイルの多重取り込みを防ぐためのテクニックです。
ここでは、#if系プリプロセッサ文の説明と、#include防御をする書き方の説明をしています。
・#if系プリプロセッサ文はコンパイル箇所を切替える文
#if系プリプロセッサ文により、一つのソースファイルの中を、コンパイルする部分とコンパイルされない部分に分けることができます。
マクロ名を使った条件を判定し、条件が成り立った所に書かれた部分がコンパイル対象となり、条件が成り立たなかった部分は、コンパイルされなくなります。
・#ifndef, #define, #endif文を使って#include防御
#if系プリプロセッサ文の中の#ifndefと#endifと、#defineを組み合わせることで、ヘッダファイルの多重取り込みを防ぐ#include防御をすることができます。
・#pragma onceも#include防御
Visual Studioでヘッダファイルを追加すると、自動的に作成される「#pragma once」でも、#include防御できます。
「#pragma once」を書いた場合は、#ifndefと#endifによる#include防御の記述は不要です。
名前と年齢をキー入力し、入力したデータを表示するプログラムが、YouTubeの動画説明に記載のURLからダウンロードできます。main.cpp, input_array.cpp, print_array.cppの3つのソースファイルに分割します。なお、ヘッダファイルは、#include防御する記述を入れます。
変数には種類があり、変数の種類によって、その変数の生存期間が異なります。変数の種類を記憶クラスと呼びます。
自動変数
関数の中で、記憶クラス指定子を付けずに宣言した変数で、関数呼出しした時に生成し、returnにより関数から呼出し元に復帰すると無くなる変数(生存期間は、関数が動いている時だけ)。
静的変数
staticという記憶クラス指定子を付けて宣言した変数で、プログラム起動時に生成し、プログラム終了時に無くなる変数(生存期間は、プログラムが動いている間ずっと)。
外部変数
関数の中で、記憶クラス指定子を付けずに宣言した変数で、プログラム起動時に生成し、プログラム終了時に無くなる変数(生存期間は、プログラムが動いている間ずっと)。
変数に付けた名前を扱える範囲を通用範囲と呼びます。通用範囲には広域的なものと局所的なものがあります。関数の外に宣言した変数をグローバル変数、関数の中に宣言した変数をローカル変数と呼んでいましたが、このグローバルが広域的という意味で、ローカルが局所的という意味になります。
自動変数は、必ず局所的です。
静的変数は、宣言した位置により、広域的静的変数と局所的静的変数に分かれます。
外部変数は、外部変数宣言(externを付けた宣言)をどの位置に書くかで、広域的なものと局所的なものに分けられますが、普通は、外部変数宣言はヘッダファイルに書くので、広域的な変数になります。
これらの変数宣言方法と、性質の違いについて動画で説明します(前編と後編に分けて説明しています)。
・スタック領域と静的領域は宣言時に決まる
スタック領域の変数は、関数の中で宣言したものです。関数呼出し時にスタック領域に作られ、関数からreturnすると、変数のための領域は無くなります。
静的領域の変数は、関数の外で宣言したグローバル変数と、関数の中でもstaticというキーワードを付けると、静的領域に変数が作られます。静的領域の変数は、プログラム起動時に作られ、プログラムが終わるまで無くなりません。
レジスタ領域の変数もありますが、現在は、変数宣言で領域獲得することはしません。
・変数の名前を使うことが出来る範囲が通用範囲
変数を宣言した場所が { } の中の場合は、その名前で変数を扱えるのは宣言した位置から、 } の位置までの間です。
{ } で全く囲われていない位置で変数を宣言した場合は、宣言した位置から、ファイルの終わりの位置までの間で、その名前の変数を扱うことが出来ます。
・記憶クラス指定子と宣言位置で記憶クラスが決まる
記憶クラスは変数の種類で、以下の3種類があります。
自動変数
静的変数(局所的静的変数と広域的静的変数がある)
外部変数(外部変数定義と外部変数宣言がある)
staticという記憶クラス指定子を付けると、宣言位置にかかわらず静的変数になります。記憶クラス指定子無しに関数の外で宣言すると外部変数になります。記憶クラス指定子無しに関数の中で宣言すると自動変数になります。
・staticは静的変数宣言でexternは外部変数宣言
staticを付けると静的変数になりますが、宣言した位置により局所的静的変数と広域的静的変数に分かれます。
externを付けると外部変数宣言になります。外部変数定義はメモリを確保しますが、外部変数宣言はメモリは確保されません。externを付けた外部変数宣言は、ヘッダファイルに書きます。
・外部変数宣言はヘッダファイルに書く
外部変数定義はソースファイル(.cppファイル)に書き、対応する外部変数宣言はヘッダファイル(.hファイル)に書きます。残りの、静的変数と自動変数は、ソースファイル(.cppファイル)に書きます。
・何の制約も無い変数は自動変数にする
非常に大きなメモリを使う場合は静的変数にします。複数の関数で共有する場合、一つのソースファイルで定義された関数間で共有する場合は広域的静的変数に、複数のソースファイルにある関数間で共有する場合は外部変数にします。
以下の記憶クラスの変数を用意します。
外部変数定義
広域的静的変数
自動変数
局所的静的変数
main関数、内部関数(staticを付けた関数)、global_func関数(staticのない関数)の中で、これらの変数を変化させることで、変数の値がどの様に変化するかを実験します。また、ヘッダファイルを作る場合、どの記述をヘッダファイルにするかを説明します。
なお、記憶クラスを宣言し、main関数で各変数を表示するだけのプログラムが、YouTubeの動画説明に記載のURLからダウンロードできます。
テスト実施フォーム ← テストチャレンジできます
上記テストの解説と解答