変数
変数の宣言
変数は、使用する前に宣言する必要があります。変数を識別するためには、一意の名称が使用されます。変数を宣言するには、型と一意の名称の指定が必要です。変数の宣言は演算ではありません。
基本データ型は下記の通りです。
- char、short、int、long、uchar、ushort、uint、ulong の 整数
- color – RGB 色を表す整数
- datetime – 日時(1970年1月1日0時からの秒数を含む符号なしの整数)
- bool – ブール値(true と false)
- double – 倍精度浮動小数点数
- float – 単精度浮動小数点数
- string – 文字列
例:
string szInfoBox; |
複雑または複合型は次の通りです。
構造体は、他の型を使用して構築された複合データ型です。
struct MyTime |
構造体を宣言するまで、その構造体型の変数を宣言することは出来ません。
配列
配列は同じ型のデータをインデックス付きで収めます。
int a[50]; // 50 の整数の 1 次元配列 |
配列のインデックスとなれるのは整数のみです。4 次元以下の配列のみが使用可能です。配列要素の番号付けは 0 から始まります。1 次元配列の最後の要素は、配列のサイズより 1 小さいインデックスを有します。例えば 50 の整数からなる配列の最後の要素の呼び出しは a[49] となります。多次元配列の次元も同じように 0 から次元サイズ−1までのインデックスを持ちます。例の 2 次元配列の最後の要素は m[6][49] となります。
静的配列は時系列としては表すことが出来ません。末尾から先頭に向かっての配列要素アクセスを設定する ArraySetAsSeries() 関数は静的配列に適応出来ません。時系列と同じアクセスを提供する場合には動的配列オブジェクトが使用されるべきです。
配列の範囲以外のアドレス指定がされた場合、実行するサブシステムは重大エラーを発生し、プログラムが停止されます。
アクセス指定子
アクセス指定は、コンパイラが変数や構造体やクラスのメンバにアクセスする方法を定義します。
const 指定子は、変数を定数として宣言し、実行時の変更を不可能にします。宣言時には、変数の単一の初期化が許可されます。
例:
int OnCalculate (const int rates_total, // price[] 配列のサイズ |
構造体やクラスのメンバにアクセスするには次の修飾子を使用します。
- public – 変数やクラスメソッドへの無制限のアクセスを許可します。
- protected – このクラスのメソッド、またpublic で継承されたクラスからのアクセスを許可します。その他のアクセスは不可能です。
- private – 同じクラスのメソッドからのみの変数やクラスメソッドへのアクセスを許可します。
- virtual – クラスメソッドのみ(構造体のメソッドは含まない)に適用され、クラスを仮想関数のテーブルに配置する必要があるとコンパイラに指示します。
ストレージクラス
ストレージクラスは 3 つあります。static、input 及び externです。これらのストレージクラス修飾子は、対応する変数がグローバルプールと呼ばれるメモリの事前割り当て領域に分布していることをコンパイラに明示的に指示します。また、これらの修飾子は、可変データの特殊な処理を示します。ローカルレベルで宣言した変数が static でない場合、そのような変数のためのメモリはプログラムスタックに自動的に割り当てられます。配列が宣言されたブロックの視認性の領域を越えると、非静的配列に割り当てられたメモリの解放が自動的に行われます。
ローカル変数
関数内で宣言された変数はローカル変数です。ローカル変数の有効範囲は、宣言された関数の内部に制限されています。ローカル変数は任意の式の結果によって初期化出来ます。関数の全ての呼び出しは、ローカル変数を初期化します。ローカル変数は、対応する関数のメモリ領域に格納されます。
例:
int somefunc() |
変数の有効範囲とは変数を参照することが出来るプログラムの部分です。(内部レベルでの)ブロック内で宣言された変数の有効範囲はそのブロックです。ブロックスコープは、変数の宣言で始まり、最後の右中括弧で終了します。
関数の先頭で宣言されたローカル変数も関数パラメータ と同じくブロックの有効範囲を持っています。ブロックは変数の宣言を含むことが出来ます。ブロックが入り子にされ、外部ブロックの識別子が内部ブロック内の識別子と同名の場合、外部ブロックの識別子は、内部ブロックの動作が終わるまで隠されています。
例:
void OnStart() |
これは、内部ブロックの実行中には、外部ブロックにおける同名の識別子の値ではなく、それ自身のローカルの識別子の値が見られることを意味します。
例:
void OnStart() |
static として宣言されたローカル変数は、プログラム開始時に存在しているにもかかわらず、有効範囲はブロックです。
スタック #
全ての MQL5 プログラムでは、自動的に作成されたローカル関数の変数を格納するために、スタックと呼ばれる特殊なメモリ領域が割り当てます。 One stack is allocated for all functions, its default size for indicators is equal to 1Mb. エキスパートアドバイザーとスクリプトでは、スタックサイズはスタックサイズをバイト単位で設定する#property stacksize コンパイラ指令を使用して管理することができ、デフォルトでは、 スタックのために8Mbのメモリが割り当てられます。
静的ローカル変数は、他の静的及びグローバル変数が格納されている、スタックとは別に存在する特殊なメモリ領域に保存されます。動的に作成された変数もスタックとは別のメモリ領域を使用します。
各関数呼び出し時に、内部の非静的変数にスタック内の場所が割り当てられます。メモリは関数の終了後に再使用出来るようになります。
1 番目の関数から 2 番目の関数が呼び出された場合、2 番目の関数は残りのスタックメモリから変数に必要なサイズを使用します。このように、付属の関数を使用する場合には、スタックメモリは順次各関数によって使用されます。これは、スタックオーバーフローと呼ばれる関数呼び出し中のメモリ不足につながる可能性があります。
従って、ローカルデータの量が大きい場合は動的メモリが使用されるべきです。関数に入る際に、ローカルに必要とされるメモリをシステムに割り当て(new、ArrayResize())、関数から出る際にメモリ解除します(delete、ArrayFree())。
仮引数
関数に渡されたパラメータはローカルです。有効範囲は関数ブロック内です。仮引数は外部変数や関数の中で定義されたローカル変数とは違う名称を持つ必要があります。関数ブロック内では仮引数に値を代入することが出来ます。仮引数が const 修飾子と宣言された場合、その値は関数内で変更することは出来ません。
例:
void func(const int & x[], double y, bool z) |
仮引数は定数で初期化出来ます。この場合には、初期化値が初期値と見なされます。横にあるパラメータも初期化する必要があります。
例:
void func(int x, double y = 0.0, bool z = true) |
このような関数を呼び出す時には初期化されたパラメータを省略することが出来ます。その場合、デフォルトが使用されます。
例:
func(123, 0.5); |
基本データ型 のパラメータは値で渡されます。この型に対応するローカル変数の関数内での変更は呼び出し元の関数には反映されません。任意の型と構造型のデータの配列は、常に参照によって渡されます。配列や構造体の内容を変更することを禁止する必要がある場合には、これらの型のパラメータは const キーワードと宣言されるべきです。
基本データ型のパラメータを参照によって渡すことも出来ます。この場合、呼び出し元の関数内でのパラメータの変更は、参照によって渡された変数にも影響を与えます。パラメータが参照によって渡されることを示すには、データ型の後に &修飾子が置かれます。
例:
void func(int& x, double& y, double & z[]) |
参照によって渡されたパラメータは、初期値で初期化することは出来ません。
関数には最大 64 のパラメータが渡されることが出来ます。
静的変数
static ストレージクラスは静的変数を定義します。static 修飾子は、データ型の前に示されます。
例:
int somefunc() |
任意の式で初期化することが出来るシンプルなローカル変数とは異なり、静的変数は型に対応する定数または定数式によっての初期化が可能です。
静的変数はプログラム実行の瞬間から存在しており、特殊な OnInit() 関数が呼び出される前に一度だけ初期化されます。初期値が指定されていない場合は、静的ストレージクラスの変数の初期値はゼロとなります。
static キーワードで宣言されたローカル変数は、関数のライフタイムを通して値を保持します。このようなローカル変数は、関数の呼び出し時には、前回の呼び出し中に持っていた値が含みます。
関数の仮引数以外の全ての変数は静的に定義出来ます。ローカルレベルで宣言した変数が静的でない場合、そのような変数のためのメモリはプログラムスタックに自動的に割り当てられます。
例:
int Counter() |
グローバル変数
グローバル変数は、関数の記述外で宣言されることによって作成されます。グローバル変数は関数と同じレベルで定義されます。つまり、ブロックにローカルではありません。
例:
int GlobalFlag=10; // グローバル変数 |
グローバル変数の有効範囲は、プログラム全体です。グローバル変数は、プログラムで定義された全ての関数からアクセス出来ます。別の初期値が明示的に定義されていない限り、グローバル変数はゼロに初期化されます。グローバル変数は、その型に対応する定数または定数式でのみ初期化することが出来ます。
グローバル変数は、プログラムがクライアント端末のメモリに読み込まれた後かつ初めの Init イベントハンドリングの前に一度だけ初期化されます。クラスオブジェクトを表すグローバル変数の場合、その初期化中に対応するコンストラクタが呼び出されます。スクリプト内ではグローバル変数は Start イベント処理の前に初期化されます。
注意事項: グローバルレベルで宣言された変数は、GlobalVariable…() 関数を使用してアクセス出来るクライアント端末のグローバル変数と混同されてはなりません。
Input変数
input ストレージクラスは、外部変数を定義しています。input 修飾子は、データ型の前に示されます。input 修飾子を持つ変数はMQL5 プログラム内で変更することはできず、読み込み専用でアクセスすることが出来ます。input 変数の値はプログラムのプロパティウィンドウから、ユーザによってのみ変更することが出来ます。
例:
//— 入力パラメータ |
Input 変数は、プログラムの入力パラメータを決定します。プログラムのプロパティウィンドウから使用出来ます。
「パラメータ( Inputs )」タブでの入力パラメータの外観を設定する方法はもう 1 つあります。入力パラメータの記述と同じ行へのコメントの追加です。これによって、入力パラメータの名称をユーザによって理解しやすくします。
例:
//— 入力パラメータ |
注意事項:配列及び複合型の変数は入力パラメータとしては使用できません。
注意事項:入力変数の文字列のコメントの長さは63文字を超えることは出来ません。
MQL5 プログラムからカスタム指標を呼び出す際のパラメータの受け渡し #
カスタム指標は iCustom() 関数を使用して呼び出されます。カスタム指標名の後には、このカスタム指標の入力変数の宣言に厳密に従ってパラメータが指定される必要があります。示されたパラメータがカスタム指標で宣言された入力変数よりも少ない場合、不足しているパラメータは、変数の宣言時に指定された値で書き入れられます。
カスタム指標が最初の種類の OnCalculate 関数を使用する場合 (すなわち、指標が同じデータ配列を使用して計算される場合)、カスタム指標を呼ぶのに ENUM_APPLIED_PRICE 値の 1 つまたは別の指標ハンドルが最後のパラメータとして使用されるべきです。入力変数に対応する全てのパラメータは明確に示されなければなりません。
入力パラメータとしての列挙
MQL5 の提供する内蔵の列挙だけでなくユーザ定義の変数も入力変数(MQL5 プログラムの入力パラメータ)として使用することが出来ます。例えば、曜日を記述する dayOfWeek 列挙体を作成し、入力変数を使用して特定の曜日を数の代わりにもっと一般的な方法で指定することが出来ます。
例:
#property script_show_inputs |
スクリプトの起動時にプロパティウィンドウからの必要な値の選択を可能にするには、プリプロセッサコマンド #property script_show_inputs を使用します。スクリプトを起動して、リストから dayOfWeek 列挙体のいずれかの値を選択することが出来ます。EnumInInput スクリプトを起動して「パラメータ( Inputs )」タブに移動します。swapday の値(3 重スワップチャージの日)はデフォルトでは水曜日(W=3)ですが、他の値を指定して、プログラムの動作を変更することが出来ます。
列挙では可能な値の数は限られています。入力値を選択するためにはドロップダウンリストが使用されます。一覧に表示された値に使用されるのは列挙型メンバのニーモニック名です。この例のようにコメントがニーモニック名に関連付けられている場合、ニーモニック名でなくコメントの内容が使用されます。
dayOfWeek 列挙の値は 0 から 6 まですが、パラメータのリストではそれぞれの値に指定されたコメントが表示されます。これは、入力パラメータの明確な記述を含むプログラムを作成するための柔軟性を提供します。
sinput 修飾子を持つ変数 #
input 修飾子を持つ変数はプログラムを起動時に外部パラメータ値を設定するだけでなくストラテジーテスターで取引ストラテジーを最適化する際にも必要とされます。文字列型のものを除く各入力変数は、最適化に使用することが出来ます。
時々、テスタ内の全てのパスの領域から外部プログラムのパラメータを除外することが必要です。sinput メモリ修飾子は、そのような場合のために導入されています。sinput は静的外部変数の宣言(sinput=静的入力)の略です。つまり、エキスパートアドバイザーコードで次の宣言
sinput int layers=6; // 層数 |
が完全な宣言に相当します。
static input int layers=6; // 層数 |
sinput 修飾子で宣言された変数は MQL5 プログラムの入力パラメータです。このパラメータの値はプログラム起動時に変更することが出来ます。この変数は、入力パラメータの最適化には使用されません。言い方を変えると、指定された条件に合う最良のパラメータセットを検索する時には、その値は列挙されません。
上に示したエキスパートアドバイザーには 5 つの外部パラメータがあります。「Number of layers」 は sinput として宣言されており 6 に等しいです。このパラメータは、取引ストラテジーの最適化中に変更することは出来ません。後で使用するために必要な値を指定することは出来ます。Start、Step と Stop のフィールドは、このような変数には使用出来ません。
よって、変数の sinput 修飾子が指定された後、ユーザがこのパラメータを最適化することは出来ません。つまり、端末ユーザは、最適化時に、ストラテジーテスターで指定された範囲内の自動列挙の初期値と最終値を設定することが出来ません。
ただし、この規則には例外が1つあります。sinput 変数は ParameterSetRange() 関数を使用して最適化タスクで変更することが出来ます。この関数は、static input( sinput )として宣言されたものを含む input 変数の使用可能な値のプログラム制御のために特異的に導入されました。ParameterGetRange() 関数は、最適化の起動時に(OnTesterInit() ハンドラで)入力変数の値を受け取り、最適化されたパラメータ値が列挙される範囲内の変更ステップ値と範囲を再設定します。
このように sinput 修飾子と入力パラメータで動作する 2 つの関数を組み合わせて、他の入力パラメータの値に依存する入力パラメータの最適化間隔を設定するための柔軟なルールを作成することが出来ます。
Extern 変数
extern キーワードは変数名の宣言に静的ストレージクラスとグローバルライフタイムの識別子として使用されます。これらの変数はプログラムの先頭から存在し、メモリはプログラムの開始直後に割り当てられ初期化されます。
複数のソースファイルで構成されたプログラムを作成することは可能です。この場合、プリプロセッサに指示する #include ディレクティブが使用されます。同じ型と識別子を持つ extern と宣言された変数は、プロジェクト内の複数のソースファイルに存在することが出来ます。
プロジェクト全体をコンパイルする時、同じ型と識別子を持つ全ての extern 変数は、グローバル変数プールのメモリの一部に関連しています。Extern 変数は、ソースファイルの分割コンパイルするのに便利です。Extern 変数が初期化出来るのは一度だけです。同じ型と識別子を持つ extern 変数の複数の初期化は禁止されています。
Extern 変数
extern キーワードは変数名の宣言に静的ストレージクラスとグローバルライフタイムの識別子として使用されます。これらの変数はプログラムの先頭から存在し、メモリはプログラムの開始直後に割り当てられ初期化されます。
複数のソースファイルで構成されたプログラムを作成することは可能です。この場合、プリプロセッサに指示する #include ディレクティブが使用されます。同じ型と識別子を持つ extern と宣言された変数は、プロジェクト内の複数のソースファイルに存在することが出来ます。
プロジェクト全体をコンパイルする時、同じ型と識別子を持つ全ての extern 変数は、グローバル変数プールのメモリの一部に関連しています。Extern 変数は、ソースファイルの分割コンパイルするのに便利です。Extern 変数が初期化出来るのは一度だけです。同じ型と識別子を持つ extern 変数の複数の初期化は禁止されています。
変数のアクセス権の有効範囲とライフタイム
有効範囲は基本的に 2 つあります。それらはローカルスコープとグローバルスコープです。
全ての関数の外で宣言された変数は、グローバルスコープに属します。このような変数はプログラムのどこからでもアクセス可能です。それらはメモリのグローバルプールに配置されているので、ライフタイムはプログラムのライフタイムと一致します。
ブロック(中括弧で囲まれたコードの一部)の内側に宣言された変数はローカルスコープに属します。このような変数は、それが宣言されているブロック外からは見えません(従って使用出来ません)。ローカル宣言の中で最も一般的なケースは、関数内で宣言された変数です。ローカルに宣言された変数は、スタック内に位置しライフタイムは関数のライフタイムに等しいです。
ローカル変数の有効範囲はそれが宣言されているブロックなので、他のブロックで宣言された変数と、またグローバルレベルまでの上位レベルで宣言された変数と同名の変数を宣言することが可能です。
例:
void CalculateLWMA(int rates_total,int prev_calculated,int begin,const double &price[]) for(i=limit;i<rates_total;i++) |
同じ行で宣言された変数 i にご注意ください。
for(int i=begin; i<limit; i++) |
i の有効範囲はループのみです。ループの外には、関数の初めで宣言された同名の変数が存在します。また、変数 k はループ本体で宣言されているので、その有効範囲はループ本体です。
ローカル変数は、アクセス指定子 static を使用して宣言することが出来ます。この場合、コンパイラはメモリのグローバルプール内に変数を有します。従って、静的変数のライフタイムは、プログラムのライフタイムに等しいです。ここではそのような変数の有効範囲は、それが宣言されているブロックに制限されています。
オブジェクトの作成と解除
MQL5 プログラムが実行のために読み込まれた後、メモリは、その型に応じて各変数に割り当てられます。すべての変数は、アクセスレベルに応じてグローバル変数とローカル変数の 2 種類に分かれています。メモリクラスによると、それらは MQL5 プログラムの入力パラメータ、静的と自動に分けられます。変数は必要に応じて対応する値に初期化されます。使用後には初期化解除され、変数によって使用されたメモリは MQL5 実行システムに戻されます。
グローバル変数の初期化と初期化解除
グローバル変数は、MQL5 プログラムが読み入れられた直後、関数が 1 つでも呼び出される前に自動的に初期化されます。初期化中に、基本データ型の変数には初期値が割り当てられ、オブジェクトにはコンストラクタ(もしあれば)が呼び出されます。入力変数はは常にグローバルレベルで宣言され、プログラム開始時にダイアログでユーザが設定した値で初期化されます。
静的変数は通常ローカルレベルで宣言されていますが、グローバル変数と同様に、メモリが前もって割り当てられ、初期化はプログラム読み込みの直後に行われます。
初期化の順序は、プログラムの変数宣言順に対応しています。初期化解除は逆の順序で実行されます。この規則は、new 演算子によって作成されていない変数に対してのみ適応します。このような変数は、プログラム読み込み後に自動的に作成/初期化され、プログラムのアンロードの前に初期化解除されます。
ローカル変数の初期化と初期化解除
ローカルレベルで宣言した変数が静的でない場合は、メモリが自動的に割り当てられます。グローバル変数と同様に、ローカル変数は、プログラムの実行が宣言を満たした瞬間に自動的に初期化されます。従って、初期化の順序は、宣言の順序に対応しています。
ローカル変数は、宣言されたプログラムブロックの最後に宣言の逆の順序で初期化解除されます。プログラムブロックは複合演算子で switch 選択演算子、反復演算子( for、while、do-while )、関数本体及び if-else 演算子の一部になることがあります。
ローカル変数は、プログラムの実行が変数宣言を満たす時に初めて初期化されます。変数が宣言されているブロックが実行されなかった場合は、そのような変数は初期化されていません。
配置されたオブジェクトの初期化と初期化解除
ポインタの宣言は、対応するオブジェクトの初期化を伴わないのでオブジェクトポインタは特殊となります。動的に配置されたオブジェクトは、new 演算子がクラスのサンプル作成した時点で初期化されます。オブジェクトの初期化は、対応するクラスのコンストラクタの呼び出しを前提としています。クラス内に対応するコンストラクタがない場合、基本データ型のメンバは自動的には初期化されません。文字列、動的配列及び複合型オブジェクト型のメンバは自動的に初期化されます。
ポインタは、ローカルまたはグローバルレベルで宣言することができ、NULLの空の値、または 同じ型か継承された 型を持つポインタの値で初期化出来ます。new 演算子がローカルレベルで宣言されたポインタに対して呼び出さた場合、このポインタの delete 演算はレベルを終了する前に実行される必要があります。さもないと、ポインタが失われ、オブジェクトの明示的な削除は失敗します。
object_pointer=new Class_name の式を使用して作成されたオブジェクトは delete(object_pointer) 演算子で削除されなければいけません。このような変数が delete 演算子 によってプログラム終了時に削除されない場合は、対応するエントリが「エキスパート」操作ログに表示されます。複数の変数を宣言し、それらの全てに 1 つのオブジェクトポインタを割り当てることは可能です。
動的に作成されたオブジェクトにコンストラクタがある場合、このコンストラクタは、new 演算子の実行の瞬間に呼び出されます。オブジェクトにデストラクタがある場合、それは delete 演算子の実行中に呼び出されます。
このように、動的に配置されたオブジェクトは new 演算子で初めて作成され、delete 演算子またはプログラムアンロード時に MQL5 の実行システムによって自動的に削除されます。 動的に作成されたオブジェクトのポインタの宣言の順序は、初期化の順序には影響しません。初期化と初期化解除の順序はプログラマによって完全に制御されます。
MQL5 における動的なメモリ割り当て
動的配列を使用すると、解放されたメモリは、すぐにオペレーティングシステムに戻されます。
new 演算子を使用して動的なクラスのオブジェクトを作成する時、メモリは初め、メモリマネージャが使用しているクラスメモリプールから要求されます。充分なメモリがプール内にない場合、メモリは、オペレーティングシステムから要求されます。delete 演算子を使用して動的なクラスのオブジェクトを削除する時、解放されたメモリはすぐにクラスメモリプールに戻されます。
メモリマネージャは OnInit()、OnDeinit()、OnStart()、OnTick()、OnCalculate()、OnTimer()、OnTrade()、OnTester()、OnTesterInit()、OnTesterPass()、OnTesterDeinit()、OnChartEvent()、OnBookEvent() イベント処理関数の終了後、解放したメモリを直ちにオペレーティングシステムに返します。
変数の特徴
作成と削除の順序、またコンストラクタとデストラクタの呼び出しに関する主な情報は以下の表に示されています。
グローバル自動変数 | ローカル自動変数 | 動的に作成されたオブジェクト | |
初期化 | MQL5 プログラム読み込みの直後 | 宣言されたコード行の実行時 | new 演算子の実行時 |
初期化順 | 宣言の順序 | 宣言の順序 | 宣言の順序と関係なし |
初期化解除 | MQL5 プログラムのアンロード前 | 宣言ブロックの実行終了時 | delete 演算子の実行時またはMQL5 プログラムのアンロード前 |
初期化解除順 | 初期化順の反対 | 初期化順の反対 | 初期化順と無関係 |
コンストラクタの呼び出し | MQL5 プログラム読み込み時 | 初期化時 | new 演算子の実行時 |
デストラクタの呼び出し | MQL5 プログラムのアンロード時 | 変数が初期化されたブロックの終了時 | delete 演算子の実行時 |
エラーログ | 自動的に作成されたオブジェクトを削除しようとする試みについて「エキスパート」操作ログにメッセージを記録 | 自動的に作成されたオブジェクトを削除しようとする試みについて「エキスパート」操作ログにメッセージを記録 | MQL5 プログラムのアンロード時に削除を取り消すに動的に作成されたオブジェクトについて「エキスパート」操作ログにメッセージを記録 |
Originally posted 2019-07-27 10:21:59.