変数には有効範囲がある
変数のスコープ(scope)とは、変数の有効範囲のことです。変数が利用可能な範囲とでも言いましょうか。その有効範囲はどこかと言うと、OnTick等の関数の{ }で囲まれた部分になります。ちなみに、{ }を”ブロック”と呼びます。
1 | void OnTick() |
2 | { |
3 | // この中が関数のブロック |
4 | } |
変数を宣言する場所によって、変数は2種類に大別されます(staticもありますが必須ではないので割愛)。関数の外に宣言した変数を”グローバル変数”、関数内に宣言した変数を”ローカル変数”と言います。
グローバル変数はプログラム内のどこでも利用できます。また、MT4を再起動したりEAを停止したりしない限り、変数の値は保持されます。
大変便利ではありますが、乱用するとバグの原因になります。「あれ、何でこの変数の値がここで0になっちゃってるんだろう?1が格納されているはずなのに…」といった感じで。
他方、ローカル変数は宣言された関数内でしか利用できません。言い換えると、他の関数でそのローカル変数を使うことはできません。また、関数を呼び出すたびにローカル変数の値は初期化されます。
この特性をうまく使って、2種類の変数を使い分けながら、プログラミングしていくことになります。
使い分けのポイントとしては、どの関数でも使う可能性のある変数だったり、関数を呼び出す際に変数を保持しておきたかったりする場合はグローバル変数として宣言し、それ以外はローカル変数として宣言することです。変数の有効範囲は基本的に狭い方が良いので、迷ったらローカル変数で事足りるか検討すると良いと思います。
では、サンプルプログラムでグローバル変数とローカル変数の挙動の違いを見てみましょう。
01 | //+------------------------------------------------------------------+ |
02 | //| Sample.mq4 | |
03 | //| Copyright (c) 2016, りゅーき | |
04 | //| https://autofx100.com/ | |
05 | //+------------------------------------------------------------------+ |
06 | #property copyright "Copyright (c) 2016, りゅーき" |
07 | #property link "https://autofx100.com/" |
08 | #property version "1.00" |
09 |
10 | //+------------------------------------------------------------------+ |
11 | //| グローバル変数 | |
12 | //+------------------------------------------------------------------+ |
13 | int globalVar = 1; |
14 |
15 | //+------------------------------------------------------------------+ |
16 | //| Expert initialization function | |
17 | //+------------------------------------------------------------------+ |
18 | int OnInit() |
19 | { |
20 | Print( "globalVar(OnInit) = " , globalVar); |
21 |
22 | // グローバル変数を上書き |
23 | globalVar = 2; |
24 |
25 | Print( "globalVar(OnInit) = " , globalVar); |
26 |
27 | // ローカル変数 |
28 | int localVar = 10; |
29 |
30 | Print( "localVar(OnInit) = " , localVar); |
31 |
32 | // 【クイズ】 |
33 | // 以下の2行のコメントを解除してEAを実行した場合、OnTick関数内のglobalVarの値は「20」になるでしょうか?それとも「1」でしょうか?それはなぜでしょうか? |
34 | // int globalVar = 20; |
35 | // Print("globalVar(OnInit) = ", globalVar); |
36 |
37 | return (INIT_SUCCEEDED); |
38 | } |
39 |
40 | //+------------------------------------------------------------------+ |
41 | //| Expert deinitialization function | |
42 | //+------------------------------------------------------------------+ |
43 | void OnDeinit( const int reason) |
44 | { |
45 | } |
46 |
47 | //+------------------------------------------------------------------+ |
48 | //| Expert tick function | |
49 | //+------------------------------------------------------------------+ |
50 | void OnTick() |
51 | { |
52 | Print( "globalVar(OnTick) = " , globalVar); |
53 |
54 | // 下記のPrint関数のコメントを解除してコンパイルするとエラーになる。 |
55 | // 理由は、ローカル変数のスコープ(有効範囲)は、ローカル変数を宣言した関数内であるため。 |
56 | // localVarはOnTick関数内で宣言されていないので、そんな変数は存在しないと怒られるのである。 |
57 | // ちなみに、コンパイルエラーメッセージは「error 256: 'localVar' - undeclared identifier」 |
58 | // 直訳すると、宣言されていない識別子となる。 |
59 | // 要するに「その変数は宣言されていないから宣言するか、変数名のつづりミスだから見直してね。」と言っているわけ。 |
60 |
61 | // Print("localVar(OnTick) = ", localVar); // これはエラーに |
62 | } |
globalVarがグローバル変数、localVarがローカル変数です。globalVarはOnInit関数内でも、OnTick関数内でも使えることが分かると思います。他方、localVarはOnInit関数内で宣言されているので、OnInit関数内でしか使えません。
試しに、OnTick関数内でlocalVarを使ってみてください(最後のPrint関数がそれに該当)。コンパイルでエラーになるはずです。
プログラム内のコメントに記載していますが、localVarはOnTick関数内で宣言されていないので、そんな変数は存在しないと怒られるわけですね。
ここで、クイズです。上記コードの中で、【クイズ】に記述されている2行のコメントを解除してEAを実行した場合、OnTick関数内のglobalVarの値は「20」になるでしょうか?それとも「1」でしょうか?それはなぜでしょうか?ぜひ実際に試して結果を確認し、考察してみてください。
変数のスコープは非常に重要な概念ですので、ここでぜひ習得しておきましょう!
グローバル変数を使う時は、グローバル変数だと分かる記号を変数名に入れておくことをお勧めします。私の場合、グローバル変数の先頭には「g」を付けるようにしています。例:gPipsPoint、gSlippage
