自動変数は、プログラムがその変数の有効範囲(スコープ) 以外の部分を実行しているときには存在しません。 例えば同じ関数を2回呼び出したとき、 1回目の呼び出しのときの変数の値は、 2回目の呼び出しのときまで保存されません。 例えば、次のプログラムでは、 sub1() の1回目の呼び出しで変数 i に 10 が代入されるものの、 2回目の呼び出しのときには i に 10 以外のものが入っています。
#include <iostream.h> // // 引数 f が 0 なら i に 10 を代入、非 0 なら i を表示する関数 // void sub1(int f) { int i; // sub1 の中だけで有効な変数(自動変数) if (f == 0) { i = 10; } else { cout << "i = " << i << endl; } } void sub2(void) { int x = 20; } int main(void) { sub1(0); // 一回目の呼び出しで sub1 の i に 10 を入れる。 sub2(); sub1(1); // 二回目の呼び出しで sub1 の i を表示すると 10 ではない。 // i が保存されていれば 10 が表示されるはず。 return 0; } |
自動変数は次のようにして定義しますが、 普通 auto は省略されます。
auto int i; |
static int i; |
このような変数を静的変数と呼びます。 静的変数の生成(メモリ割り当てや初期化)はプログラムの実行開始時に行われ、 プログラムが終了するまで保存されます。 上のプログラムにおいて sub1() の自動変数 i を保存するには、static int i; と定義します。こうすると2回目の呼び出しのときにも i には 10 が入っています。
#include <iostream.h> int inc(void) { int n = 0; return n++; } int main(void) { int i; for (i = 0; i < 10; i++) { cout << "inc() = " << inc() < < endl; } return 0; } |
プログラム中のどこかに static を書き加え、 上記の目的を果たすようにしてください。 修正したソースプログラムをメールに添付して、 tokoi まで送ってください。Subject:(件名)は kadai29 としてください。
#include <iostream.h> void sub(void) { int i; // 関数 sub() の中だけで有効 for (i = 0; i < 10; i++) { cout.width(3); // 出力欄の幅(桁数)を3に設定 cout << i; } cout <<endl; } int main(void) { int i; // 関数 main の中だけで有効 for (i = 0; i < 10; i++) { sub(); } return 0; } |
for によって繰り返されている { ... } の部分も ひとつのブロックです。
#include <iostream.h> int main(void) { <---------------+ int i; | | for (i = 0; i < 10; i++) { <-----------+ | int j; | | | | for (j = 0; j < 10; j++) { <-------+ | | cout.width(3); | | | cout << j; (1) (2) (3) } <-------+ | | cout << endl; | | } <-----------+ | | return 0; | } <---------------+ |
上の例において (2) のブロックで定義されている変数 j は、その内側のブロック (1) でも利用できますが、 ブロック (2) の外側では利用できません。 このような変数の有効範囲のことを、 スコープ(可視範囲)といいます。
#include <iostream.h> int main(void) { for (;;) { cout << "y(es) か n(o) を入れてください:"; char yesno[5]; // yes + '\n' + '\0' の5文字 cin.getline(yesno, sizeof yesno); if (*yesno == 'y' || *yesno == 'n') // 最初の1文字だけで判別する break; } cout << "入力したのは " << yesno << " です" << endl; return 0; } |
修正したソースプログラムをメールに添付して、 tokoi まで送ってください。Subject:(件名)は kadai30 としてください。
#include <iostream.h> int sum; // 共有しようとする変数 void goukei(int n) { while (n > 0) { sum += n--; } } void hyouji(void) { cout << "合計は " << sum; } int main(void) { sum = 0; goukei(100); hyouji(); return 0; } |
このような変数は大域変数(グローバル変数、外部変数)と呼ばれます。 大域変数は特定のブロックには関連づけられていないので、 プログラムの実行開始時に生成されます。 すなわち大域変数は静的変数です。
大域変数の定義が、 その変数を利用している部分より前にないときは、 その部分より前で 「どこかにそう言う変数があるよ」という宣言をします。
extern int x; |
この宣言自体は x という変数の生成を行いませんが、 どこか他のところで定義されている x という大域変数を利用することを宣言しています。 この宣言は関数の内側・外側のいずれにも置くことができます。 関数の内側においた場合は、そのブロック内でのみ有効になります。
#include <iostream.h> extern int x; int addx(int x) { return ::x + x; // ::x は大域変数の x } int main(void) { extern int y; // この y は main() の中でのみ利用可能 cout << "x + y = " << addx(y) << endl; return 0; } int x = 10, y = 20; // 大域変数の定義(これが実体) |
仮引数や局所変数が大域変数と同じ変数名のときは、 仮引数や局所変数が優先され大域変数は隠蔽されてしまいます。 この場合、大域変数を利用するには、 クラス名を伴わないスコープ演算子 :: (グローバルスコープ演算子)を用います。
#include <iostream.h> void goukei(int n) { while (n > 0) { sum += n--; } } void hyouji(void) { cout << "合計は " << sum << endl; } int sum; // 共有しようとする変数 int main(void) { sum = 0; goukei(100); hyouji(); return 0; } |
修正したソースプログラムをメールに添付して、 tokoi まで送ってください。Subject:(件名)は kadai31 としてください。
#include <iostream.h> extern void swap(int &, int &); // これが関数プロトタイプ int main(void) { int x, y; x = 10; y = 20; cout << "x=" << x << ", y=" << y; cout << " のとき x と y を入れ替えると "; swap(x, y); cout << "x=" << x << ", y=" << y; cout << endl; return 0; } // // 関数 swap() の定義(これが実体) // void swap(int &a, int &b) { int t; t = a; a = b; b = t; } |
この例では関数 swap の戻り値の型が void(値を返さない)で、 2つの引数がいずれも int 型の参照渡しであることを示しています。
goukei.cc |
---|
extern int sum; // どこか他で定義されている // // 関数 goukei() の定義 // void goukei(int n) { while (n > 0) { sum += n--; } } |
hyouji.cc |
---|
#include <iostream.h> extern int sum; // どこか他で定義されている // // 関数 hyouji() の定義 // void hyouji(void) { cout << "合計は " << sum; } |
main.cc |
---|
int sum; // 共有しようとする変数の定義 extern void goukei(int), hyouji(void); // 関数プロトタイプ int main(void) { sum = 0; goukei(100); hyouji(); return 0; } |
この一つ一つのファイルがコンパイル単位になります。 このようにファイルを分けた場合は、 次のようにしてコンパイルします。
% CC main.cc goukei.cc hyouji.cc -o prog[Enter] |
これによって main.cc goukei.cc hyouji.cc をそれぞれコンパイルし、 作成されたオブジェクトファイルを結合して prog という実行ファイルを 作成します(-o prog を省略すれば実行ファイルのファイル名は a.out になります)。
この例では、大域変数 sum (の実体)は main.cc の中で定義されており、 goukei.cc、hyouji.cc では、 下の宣言によってそれぞれのソースプログラムの中以外に sum が用意されていることを示しています。
extern int sum; |
大域変数の定義に static を加えると、この変数は定義されているコンパイル単位内の、 定義している場所より後のみで有効になり、 他のコンパイル単位からは利用できなくなります。
static int sum; |
関数も変数同様 static を付けて定義することで、 他のコンパイル単位からは利用できなくなります。
static void goukei(int n) { while (n > 0) { sum += n--; } } |
したがって、特定のコンパイル単位内でしか使用しない大域変数や関数は、 static を付けて定義すれば他のコンパイル単位からは隠すことができます。 こうすると仮に他のコンパイル単位で同じ名前の変数や関数を定義していたとしても、 それと衝突する(コンピュータがどちらを使えばいいか分からなくなる)ことがなくなるので、 プログラムの保守性が向上します。
加速度 a で t 秒間加速したとき、 速度は v = at + v0、位置は s = at2 / 2 + s0 です。 これを X 方向と Y 方向のそれぞれについて計算します。 速度と位置の初期値は 0 とします。
X 方向と Y 方向について、加速度と時間を設定する関数をそれぞれ accelerateX(), accelerateY()、現在の速度を取り出す関数をそれぞれ velocityX(), velocityY()、現在の位置を取り出す関数をそれぞれ positionX(), positionY() とします。
次の3つのソースファイルを、 それぞれ movex.cc movey.cc locate.cc として作成してください。
movex.cc |
---|
double velocity = 0.0, position = 0.0; // // t 秒後の移動量を求める // v: t 秒後の速度の増加分 // double shift(double v, double t) { return (v / 2.0 + velocity) * t; } // // 加速度と時間の設定 // void accelerateX(double a, double t) { double v = a * t; // 速度の増加分 position += shift(v, t); // 移動量を現在位置に加える velocity += v; // 速度の増加分を現在速度に加える } // // 速度を取り出す // double velocityX(void) { return velocity; } // // 位置を取り出す // double positionX(void) { return position; } |
movey.cc |
---|
double velocity = 0.0, position = 0.0; // // t 秒後の移動量を求める // v: t 秒後の速度の増加分 // double shift(double v, double t) { return (v / 2.0 + velocity) * t; } // // 加速度と時間の設定 // void accelerateY(double a, double t) { double v = a * t; // 速度の増加分 position += shift(v, t); // 移動量を現在位置に加える velocity += v; // 速度の増加分を現在速度に加える } // // 速度を取り出す // double velocityY(void) { return velocity; } // // 位置を取り出す // double positionY(void) { return position; } |
locate.cc |
---|
#include <iostream.h> extern void accelerateX(double, double), accelerateY(double, double); extern double velocityX(void), velocityY(void); extern double positionX(void), positionY(void); int main(void) { double x, y, t; cout << "X 方向の加速度を入力してください: "; cin >> x; cout << "Y 方向の加速度を入力してください: "; cin >> y; cout << "加速時間を入力してください: "; cin >> t; accelerateX(x, t); accelerateY(y, t); cout << "現在の位置ベクトルは " << "(" << positionX() << ", " << positionY() << ")" << endl; cout << "現在の速度ベクトルは " << "(" << velocityX() << ", " << velocityY() << ")" << endl; return 0; } |
このプログラムを次のようにしてコンパイル/リンクしてください。
% CC locate.cc movex.cc movey.cc[Enter] |
修正したソースプログラムをメールに添付して、 tokoi まで送ってください。Subject:(件名)は kadai32 としてください。