文字や文字列の中には、重要なものがいくつかあります。これらは、俗に演算記号と言われるもので、以下のものが挙げられます。
+ - * / %
:算術演算子&& ||
:論理理演算子= += *=
:代入演算子
演算記号は式(例えば、if文の条件判定等)の中で使用されます。
これには、括弧、中括弧、カンマ、コロン、セミコロンが含まれます。
演算記号、句読点、スペースによって、言語要素を分離して使用します。
式
式は、1つ以上のオペランド(被演算子)と演算記号で構成されています。
式は、複数行にわたって記述することができます。
//複数の式が1行に記述されている
a++;
b = 10;
//1つの式が、2行に分割されている
x = (y * z) /
(w + 2) + 127;
式は、セミコロン演算子(;)で終了します。
参照:優先順位のルール
算術演算子
算術演算には、加法と乗法の演算が含まれます。
i = j + 2; //変数の加算
i = j - 3; //変数の減算
x = - x; //符号の反転
z = 3 * x; //変数の乗算
i = j / 5; //除算
minutes = time % 60; //除算の余り
i++; //変数に 1 を加算する
++i; //変数に 1 を加算する
k--; //変数から 1 を減算する
--k; //変数から 1 を減算する
インクリメントとデクリメント演算は変数だけに適用され、定数に適用することは出来ません。
プレインクリメント( ++i )とプレデクリメント( –k )は、この変数が式の中で使用される直前に適用されます。
ポストインクリメント( i++ )とポストデクリメント( k– )は、この変数が式の中で使用された直後に適用されます。
重要実行
int i = 5;
int k = i++ + ++i;
上記の式を、別のプログラミング環境から移行(例えば、Borland C++からMQL4へ)する際に、計算上の問題が発生する可能性があります。
計算の順序はコンパイラの実装に依存します。
実際には、ポストデクリメント(ポストインクリメント)を実装する方法は2つあります。
- ポストデクリメント(ポストインクリメント)は、式全体の計算後に変数に適用されます。
- ポストデクリメント(ポストインクリメント)は、操作時に直ちに変数に適用されます。
MQL4では、1つ目の方法によって処理されています。
この特性を知っていたとしても、前述の1つ目の方法で処理するのが、推奨されています。
int a = 3;
a++; // 有効な式
int b = (a++) * 3; // 無効な式
参照:優先順位のルール
代入演算子
右辺で行われる演算(例えば a + b)を含む式の値が、代入後の左辺のオペランド(被演算子)の値です。
y = x; // y 変数に x 値を代入
y += x; // y 変数に x を加算
y -= x; // y 変数から x を減算
y *= x; // y 変数 に x を乗算
y /= x; // y 変数を x で除算
y %= x; // y 変数を x で除算した余り
y >>= x; // y の 2 進数表現を右に x ビットだけシフトする
y <<= x; // y の 2 進数表現を左に x ビットだけシフトする
y &= x; // y と x の 2 進数表現の AND ビット演算
y |= x; // y と x の 2 進数表現の OR ビット演算
y ^= x; // y と x の 2 進数表現の XOR ビット演算
ビット演算は整数のみに適用できます。ビット演算は内容が難しく、使用機会がほとんどないと言っても過言ではありません。
代入演算子は、式の中で複数回使用出来ます。
この場合、式の処理は左から右に実行されます。
int x, y;
y = x = 3;
まず、変数 x の値が 3 に割り当てられ、その後 y に変数 x の値(この場合は 3 )が割り当てられます。
参照:優先順位のルール
関係演算子
論理値の偽( FALSE )は整数のゼロで表されます。一方、論理値の真( TRUE )は任意のゼロ以外の値で表されます。
関係の演算子や、論理演算を含む式の値は FALSE(0)か TRUE(1)です。
a == b; // a と b が等しければ True
a != b; // a と b が等しくなければ True
a < b; // a が b より小さければ True
a > b; // a が b より大きければ True
a <= b; // a が b 以下であれば True
a >= b; // a が b 以上であれば True
2 つの浮動小数点数を(==)で比較すること出来ません。
同一の数値に見えても、ほとんどの場合で、小数点第 15 位の値が異なるため、不均等であることがあります。
これらを正しく比較するためには、NormalizeDouble等を用いて、正規化する必要があります。
bool CompareDoubles(double number1, double number2)
{
if(NormalizeDouble(number1 - number2, 8) == 0)
return(true);
else
return(false);
}
void OnTick()
{
double first = 0.3;
double second = 3.0;
double third = second - 2.7;
if(first != third)
{
if(CompareDoubles(first, third))
printf("%.15f and %.15f are equal", first, third);
printf("%.16f and %.16f are equal", first, third);
}
}
// 結果: 0.300000000000000 0.300000000000000 are equal
// 結果: 0.3000000000000000 0.2999999999999998 are equal
参照:優先順位のルール
論理演算子
論理否定 NOT( ! )
論理否定( ! )の演算対象となる値は、算術型でなければなりません。
演算対象の値が FALSE( 0 )の場合、結果は TRUE( 1 )になり、演算対象の値が FALSE( 0 )でない場合、結果はFALSE( 0 )になります。
if(!a)
Print("aではない");
OR( || )の論理演算
任意の二つの値(この場合は、x が0より小さい”または” x が 1000 以上)のどちらかが条件を満たす場合、式の値は TRUE( 1 )になります。その他の場合は FALSE( 0 )になります。
if(x < 0 || x >= 1000)
Print("範囲外");
AND( && )の論理演算
任意の二つの値(この場合は、x が0より大きい”かつ” x が 1000 以下)の両方の条件を満たす場合、式の値は TRUE( 1 )になります。その他の場合は FALSE( 0 )になります。
if(x > 0 && x <= 1000)
Print("範囲内");
ビット演算子
1の補数
変数値の1の補数。式の値は、2 進数での各桁で、変数の桁の値が 1 の場合には 0、0 の場合には 1 となります。
char a = 'a', b;
b = ~a;
Print("a = ", a, " b = ", b);
// 結果:
// a = 97 b = -98
右シフト
x の 2 進数表現が、 y 桁だけ右にシフトされます。シフトする値が符号なしの型の場合、論理右シフトが行われ、解放された左側のビットはゼロで満たされます。
シフトする値が符号付きの型の場合、算術右シフトが行われ、解放された左側のビットは符号ビットの値で満たされます(数が正であれば、符号ビットの値は 0 で、数が負であれば符号ビットの値は 1 )。
char a = 'a', b = 'b';
Print("Before: a = ", a, " b = ", b);
//--- 右にシフトする
b = a >> 1;
Print("After: a = ", a, " b = ", b);
// 結果:
// Before: a = 97 b = 98
// After: a = 97 b = 48
左シフト
xの 2 進数表現が y 桁だけ左にシフトされ、解放された右側のビットはゼロで満たされます。
char a = 'a', b = 'b';
Print("Before: a = ", a, " b = ", b);
//--- 左へシフトする
b = a << 1;
Print("After: a = ", a, " b = ", b);
// 結果:
// Before: a = 97 b = 98
// After: a = 97 b = -62
シフトされる変数の長さ以上の数でのシフトは、結果が定義されていないために推奨されません。
ANDビット演算
x と y の 2 進数表現の AND ビット演算式の値は、2 進数での各桁の内、x とy 両方がゼロ以外を含む桁は 1( TRUE )、それ以外は 0( FALSE )となります。
char a = 'a', b = 'b';
//--- AND 演算
char c = a & b;
Print("a = ", a, " b = ", b);
Print("a & b = ", c);
// 結果:
// a = 97 b = 98
// a & b = 96
ORビット演算
y と x の 2 進数表現の OR ビット演算式の値は、2 進数での各桁のうち x または y がゼロ以外を含む桁は 1(TRUE)、それ以外は 0( FALSE )となります。式の値は、2 進数での各桁のうち x と y 両方がゼロ以外を含む桁は 1(TRUE)、それ以外は0( FALSE )となります。
char a = 'a', b = 'b';
//--- OR 演算
char c = a | b;
Print("a = ", a, " b = ", b);
Print("a | b = ", c);
// 結果:
// a = 97 b = 98
// a | b = 99
XORビット演算
y と x の 2 進数表現の XOR ビット演算式の値は、2 進数での各桁のうち x と y が違う値を含む桁は1( TRUE )、それ以外は 0( FALSE )となります。
char a = 'a', b = 'b';
//--- XOR 演算
char c = a ^ b;
Print("a = ", a, " b = ", b);
Print("a ^ b = ", c);
// 結果:
// a = 97 b = 98
// a ^ b = 3
ビット演算は整数のみに適用出来ます。
その他の演算子
インデックス作成 ( [ ] )
配列の i 番目の要素にアドレス指定された場合、式の値は、i の通し番号を持つ変数の値になります。
array[i] = 3; // 配列の i 番目の要素に 3 を割り当てる
配列のインデックスとして割り当て出来るのは整数のみです。
配列は4次元まで使用可能です。
それぞれの配列のインデックスは、0 から 配列のサイズ-1 です。例えば、50 の要素からなる1次元配列がある場合、最初の要素は array[0]となり、最後の要素は array[49] となります。
配列の範囲外のアドレスが指定された場合、コンパイルエラーによりプログラムが停止します。
関数の呼び出し
関数を呼び出すためには、関数名の後ろに()を記載する必要があります。
関数に渡す引数は、対応する型(定数、変数)である必要があります。
渡された引数が複数ある場合は、カンマで区切って指定し、呼び出された関数名の後ろの丸括弧の中に書く必要があります。
式の値とは、関数によって返される値(戻り値)です。
関数の型が void の場合、関数の戻り値はありません。
関数に戻り値がある場合、関数の呼び出しを代入演算子の右側で行うことができます。
int length = 1000000;
string a = "a", b = "b", c;
//---その他の演算子
int start = GetTickCount(), stop;
for(int i = 0; i < length; i++)
{
c = a + b;
}
stop = GetTickCount();
Print("time for 'c = a + b' = ", (stop - start), " milliseconds, i = ", i);
カンマ( , )
カンマで区切られた式は、左から右に実行されます。
左側の式の演算が先に行われ、その後右側の式の演算が行われます。
結果の型と値は、右の式のものと一致します。
for(i = 0, j = 99; i < 100; i++, j--)
Print(array[i][j]);
ドット( . )
ドット演算子は、構造体とクラスの public メンバへのアクセスを行うために使用します。
struct SessionTime
{
string sessionName;
int startHour;
int startMinutes;
int endHour;
int endMinutes;
} st;
st.sessionName = "Asian";
st.startHour = 0;
st.startMinutes = 0;
st.endHour = 9;
st.endMinutes = 0;
スコープ演算子( :: )
MQL4の関数(Print()関数等)はグローバルな範囲で実行されます。
インポートした関数は、対応するインポートの範囲で呼ばれます。
クラスのメソッド関数は、対応するクラスの範囲を持ちます。
スコープ名が無い場合は、グローバルな範囲で使用する事を意味しています。
スコープ演算子が無い場合は、関数が最も近い範囲で検索されます。関数がローカルな範囲に無い場合は、グローバルな範囲で検索されます。
スコープ演算子は、クラスメンバの関数定義にも使用されます。
class CCheckContext
{
int m_id;
public:
CCheckContext() { m_id = 1234; }
protected:
int GetLastError() { return(m_id); }
};
class CCheckContext2 : public CCheckContext
{
int m_id2;
public:
CCheckContext2() { m_id2 = 5678; }
void Print();
protected:
int GetLastError() { return(m_id2); }
};
void CCheckContext2::Print()
{
::Print("Terminal GetLastError", ::GetLastError());
::Print("kernel32 GetLastError", kernel32::GetLastError());
::Print("parent GetLastError", CCheckContext::GetLastError());
::Print("our GetLastError", GetLastError());
}
void OnStart()
{
//---
CCheckContext2 test;
test.Print();
}
sizeof演算子
sizeof 演算子を使用すると、識別子または型に対応するメモリのサイズを取得することが出来ます。
sizeof演算子の後ろの()内に、取得したいデータを記載します。
void 型の名称及び関数は使用出来ません。
静的配列を指定した場合は、配列全体のサイズ(要素数×配列数×要素型サイズ)を取得します。
動的配列を指定した場合は、 動的配列のサイズを取得します。
構造体またはクラス型や、構造体またはクラス型の識別子に適用された場合は、構造体またはクラスの実際のサイズを取得します。
struct myStruct
{
char h;
int b;
double f;
} str;
Print("sizeof(str) = ", sizeof(str));
Print("sizeof(myStruct) = ", sizeof(myStruct));
サイズは、コンパイル段階で計算されます。
優先順位のルール
優先度の高い順に、票の上から記載してまとめています。
MQL4言語での演算の優先順位は、C++ で採用されている優先順位に依存します。
演算子 | 内容 | 実行順序 |
---|---|---|
() [] . | 関数の呼び出し 配列要素への参照 構造体メンバへの参照 | 左から右 |
! ~ – ++ — (type) sizeof | 論理否定 ビット否定(補数) 符号変換 インクリメント デクリメント 型変換(キャスト) バイトサイズの決定 | 右から左 |
* / % | 乗算 除算 剰余 | 左から右 |
+ – | 加算 減算 | 左から右 |
<< >> | 左シフト 右シフト | 左から右 |
< <= > >= | より小さい 以下 より大きい 以上 | 左から右 |
== != | 等しい 等しくない | 左から右 |
& | ANDビット演算 | 左から右 |
^ | XORビット演算 | 左から右 |
| | ORビット演算 | 左から右 |
&& | 論理積演算 | 左から右 |
|| | 論理和演算 | 左から右 |
?: | 条件演算 | 右から左 |
= *= /= %= += -= <<= >>= &= ^= |= | 代入 乗算代入 除算代入 剰余代入 加算代入 減算代入 左シフト代入 右シフト代入 ビット論理積代入 ビット排他的論理和代入 論理和代入 | 右から左 |
, | カンマ | 左から右 |