【MQL5】StructToCharArray関数について

MQL5リファレンス
スポンサーリンク
スポンサーリンク

StructToCharArray関数の働き・役割

StructToCharArray関数は、任意の構造体バイト(byte)配列に変換する関数です。StructToCharArray関数を使用すると、構造体の内容を文字配列として扱うことができ、データのシリアライズ(データを連続した形式に変換して保存や転送を容易にすること)やファイルへの書き込みなどに便利です。

MQL5でデータのバイナリ操作(データをバイト(byte)単位で処理すること)が必要な場合に頻繁に使用されます。

StructToCharArray関数の引数について

StructToCharArray関数引数構成は次の通りです。

bool  StructToCharArray(
   const void& struct_object,    // 構造体
   uchar&      char_array[],     // 配列
   uint        start_pos=0       // 変換を開始する配列の開始位置(初期値は0)
   );

第1引数struct_object
種類:任意の構造体への参照
説明:変換対象の構造体です。

※参照という概念にピンとこない方は、下記の参照渡しに関する記事をご確認ください。

第2引数char_array
種類:unsigned char配列
説明:変換後のバイト(byte)配列(データをバイト(byte)単位で格納する配列)を格納する配列です。

第3引数start_pos
種類:unsigned int(初期値は0)
説明:変換を開始する配列の位置です。この引数は省略可能で、初期値は0です。

StructToCharArray関数の戻り値について

StructToCharArray関数は、変換が成功するとtrueを返し、失敗するとfalseを返します。

失敗時には、GetLastError関数を使用してエラーコードを取得し、原因を特定することができます。

GetLastError関数についての詳細は↓の記事をご参照ください。

StructToCharArray関数を使う際の注意点

StructToCharArray関数を使用する際には、以下の点に注意する必要があります。

  1. 変換先の配列は十分なサイズが確保されていることを確認する必要があります。必要なサイズは構造体のサイズと同じか、それ以上であるべきです。
  2. 構造体メンバーがポインタや動的メモリを使用している場合、そのメンバーは正しく変換されません。この関数は、基本的なデータ型のみを含むPOD(Plain Old Data)型の構造体に適しています。
  3. 第3引数で指定する開始位置が、配列の範囲内であることを確認する必要があります。範囲外を指定すると、意図しない動作が発生する可能性があります。

StructToCharArray関数を使ったサンプルコード

// MyStructという構造体を定義します
struct MyStruct
{
    int a;          // 整数型のメンバー
    double b;       // 浮動小数点数型のメンバー
    char c;         // 文字型のメンバー
};

// OnStart関数はスクリプトの実行が開始されたときに自動的に呼び出されます
void OnStart()
{
    // MyStruct型の構造体インスタンスを初期化します
    MyStruct myStruct;
    myStruct.a = 42;
    myStruct.b = 3.14159;
    myStruct.c = 'M';

    // 構造体を格納するための文字配列を用意します
    uchar charArray[sizeof(myStruct)];

    // StructToCharArray関数を使用して構造体を文字配列に変換します
    bool result = StructToCharArray(myStruct, charArray);

    // 変換の成否をエキスパートログに出力します
    if(result)
    {
        Print("構造体の変換に成功しました");
        // 変換された文字配列の内容をエキスパートログに出力します
        for(int i = 0; i < sizeof(charArray); i++)
        {
            Print("charArray[", i, "] = ", (int)charArray[i]);
        }
    }
    else
    {
        Print("構造体の変換に失敗しました");
    }
}

サンプルコードに使われた関数や文法要素の簡単な解説

  1. structは、複数のメンバ変数を持つカスタムデータ型を定義するために使用されます。サンプルコードでは、整数型、浮動小数点数型、文字型のメンバーを持つMyStructという構造体を定義しています。
  2. sizeof演算子は、指定されたデータ型変数のサイズをバイト(byte)単位で返します。サンプルコードでは、構造体のサイズを計算して、配列のサイズを決定しています。
  3. StructToCharArray関数は、構造体配列に変換します。サンプルコードでは、MyStruct型の構造体を文字配列に変換しています。
  4. Print関数 は、引数に指定された文字列をエキスパートログに出力する関数です。サンプルコードでは、変換の成否や配列の内容を出力しています。

StructToCharArray関数とCharArrayToStruct関数との違いについて

StructToCharArray関数CharArrayToStruct関数は、相互に補完する関数です。StructToCharArray関数構造体バイト(byte)配列に変換し、CharArrayToStruct関数バイト(byte)配列構造体に変換します。

これにより、データのシリアライズ(データを連続した形式に変換して保存や転送を容易にすること)とデシリアライズ(バイト(byte)配列などの連続したデータを元の構造体やオブジェクトに復元すること)が可能になります。

StructToCharArray関数でシリアライズしたデータは、CharArrayToStruct関数を使用して元の構造体に復元できます。

このように、StructToCharArray関数を使用すると、構造体のデータを柔軟に扱うことができます。バイナリ操作(データをバイト(byte)単位で処理すること)が必要な場面で特に有用です。

出力結果に関する補足説明

サンプルコードを実行すると、構造体MyStructの内容がバイト(byte)配列に変換され、エキスパートログに出力されます。

この出力結果について、いくつか想定される疑問に対して、解説します。

疑問1: myStruct.c = 'M';に文字列を代入。整数型であるchar型に文字列を代入する意図について

char型は、1バイト(byte)の整数型として扱われますが、文字を表すためにも使用されます。具体的には、文字のASCIIコードを整数として格納する仕組みです。

char c = 'M';
Print((int)c);  // 77が出力される

このコードでは、文字’M’をchar型変数代入しています。これは、実際には’M’のASCIIコードである77が変数cに格納されることを意味します。

char型を用いることで、文字とそのASCIIコードを相互に扱うことができます。

したがって、サンプルコードでmyStruct.c = 'M';とすることで、構造体cメンバーに’M’のASCIIコード(77)を代入しているのです。これはchar型の標準的な使用法です。

疑問2: sizeof(charArray)の値はいくつになるか?

sizeof(charArray)の値は、charArray要素数に依存します。サンプルコードでは、charArrayは次のように定義されています。

uchar charArray[sizeof(myStruct)];

この場合、sizeof(myStruct)charArrayのサイズを決定します。myStructは以下のように定義されています。

struct MyStruct
{
    int a;          // 整数型のメンバー(4バイト)
    double b;       // 浮動小数点数型のメンバー(8バイト)
    char c;         // 文字型のメンバー(1バイト)
};

それぞれのメンバーのサイズは以下の通りです。

したがって、myStructの合計サイズは

4バイト(byte) + 8バイト(byte) + 1バイト(byte) = 13バイト(byte)になります。

よって、uchar charArray[sizeof(myStruct)]のサイズは13バイト(byte)となり、sizeof(charArray)は13を返します。

疑問3: charArray[0] = 42はなぜmyStruct.aに代入された42と同じか?

構造体MyStructメンバaint型で、値42が代入されています。整数42はそのままの値がリトルエンディアン形式でバイト(byte)配列に格納されます。

リトルエンディアンについての詳細は↓の記事をご参照ください。

リトルエンディアン形式では、最下位バイト(byte)から順にメモリに格納されるため、charArray[0]には42がそのまま入ります。

疑問4: myStruct.b = 3.14159はなぜcharArray[1]からcharArray[11]までに分割されて表現されるか?

浮動小数点数3.14159はdouble型であり、IEEE 754標準に基づいてメモリに格納されます。

double型の値は64ビット(8バイト)で、符号ビット、指数部、仮数部に分割されます。

この値はリトルエンディアン形式で次のようにバイト(byte)列に格納されます:

  • 0x1F 0x85 0xEB 0x51 0xB8 0x1E 0x09 0x40

それぞれのバイト(byte)が、charArray配列に格納されます。

このように、浮動小数点数のメモリ表現がバイト(byte)配列に変換されるため、出力結果に分割された値が表示されます。

正しいエキスパートログの出力結果

以下はサンプルコードを実行した際のエキスパートログの出力結果です:

構造体の変換に成功しました
charArray[0] = 42
charArray[1] = 0
charArray[2] = 0
charArray[3] = 0
charArray[4] = 31
charArray[5] = 133
charArray[6] = 235
charArray[7] = 81
charArray[8] = 184
charArray[9] = 30
charArray[10] = 9
charArray[11] = 64
charArray[12] = 77

この出力結果により、次のことが確認できます:

  1. charArray[0]には整数42がそのまま格納されています。
  2. charArray[1]からcharArray[11]までには、浮動小数点数3.14159がIEEE 754標準に基づいてエンコードされて格納されています。
  3. charArray[12]には文字’M’のASCIIコード(77)が格納されています。

これにより、構造体の各メンバーがどのようにバイト(byte)配列に変換されるかを理解することができます。

浮動小数点数のメモリ表現に馴染みがない場合、この仕組みを理解するのは少し難しいかもしれませんが、IEEE 754標準とリトルエンディアン形式を理解することで納得できるでしょう。

タイトルとURLをコピーしました