Kiểm soát thứ tự byte trong số nguyên
Các hệ thống thông tin khác nhau, ở cấp độ phần cứng, sử dụng thứ tự byte khác nhau khi biểu diễn số trong bộ nhớ. Do đó, khi tích hợp các chương trình MQL với "thế giới bên ngoài", đặc biệt là khi triển khai các giao thức giao tiếp mạng hoặc đọc/ghi tệp ở các định dạng phổ biến, có thể cần thay đổi thứ tự byte.
Máy tính Windows áp dụng thứ tự little-endian (bắt đầu từ byte ít ý nghĩa nhất), tức là byte thấp nhất xuất hiện đầu tiên trong ô nhớ được cấp phát cho biến, sau đó là byte với các bit cao hơn, và cứ tiếp tục như vậy. Ngược lại, thứ tự big-endian (bắt đầu từ chữ số cao nhất, byte quan trọng nhất) được sử dụng rộng rãi trên Internet. Trong trường hợp này, byte đầu tiên trong ô nhớ là byte với các bit cao, và byte cuối cùng là bit thấp. Thứ tự này tương tự như cách chúng ta viết số "từ trái sang phải" trong đời sống thường ngày. Ví dụ, giá trị 1234 bắt đầu bằng 1 và đại diện cho hàng nghìn, tiếp theo là 2 cho hàng trăm, 3 cho hàng chục, và cuối cùng 4 chỉ là bốn (thứ tự thấp).
Hãy xem thứ tự byte mặc định trong MQL5. Để làm điều này, chúng ta sẽ sử dụng script MathSwap.mq5
.
Nó mô tả một mẫu ghép nối cho phép chuyển đổi một số nguyên thành mảng các byte:
template<typename T>
union ByteOverlay
{
T value;
uchar bytes[sizeof(T)];
ByteOverlay(const T v) : value(v) { }
void operator=(const T v) { value = v; }
};
2
3
4
5
6
7
8
Mã này cho phép bạn phân chia số thành các byte một cách trực quan và liệt kê chúng bằng các chỉ số từ mảng.
Trong OnStart
, chúng ta mô tả biến uint
với giá trị 0x12345678 (lưu ý rằng các chữ số là thập lục phân; trong cách ghi này, chúng tương ứng chính xác với ranh giới byte: cứ mỗi 2 chữ số là một byte riêng biệt). Hãy chuyển đổi số này thành mảng và xuất ra nhật ký.
void OnStart()
{
const uint ui = 0x12345678;
ByteOverlay<uint> bo(ui);
ArrayPrint(bo.bytes); // 120 86 52 18 <==> 0x78 0x56 0x34 0x12
...
2
3
4
5
6
Hàm ArrayPrint
không thể in số ở dạng thập lục phân, vì vậy chúng ta thấy biểu diễn thập phân của chúng, nhưng việc chuyển đổi chúng sang cơ số 16 rất dễ dàng và đảm bảo chúng khớp với các byte ban đầu. Trực quan, chúng đi theo thứ tự ngược: tức là dưới chỉ số 0 trong mảng là 0x78, sau đó là 0x56, 0x34 và 0x12. Rõ ràng, thứ tự này bắt đầu bằng byte ít ý nghĩa nhất (thực tế, chúng ta đang ở trong môi trường Windows).
Bây giờ, hãy làm quen với hàm MathSwap
, mà MQL5 cung cấp để thay đổi thứ tự byte.
integer MathSwap(integer value)
Hàm này trả về một số nguyên trong đó thứ tự byte của đối số được truyền vào bị đảo ngược. Hàm nhận các tham số kiểu ushort/uint/ulong
(tức là kích thước 2, 4, 8 byte).
Hãy thử hàm này trong thực tế:
const uint ui = 0x12345678;
PrintFormat("%I32X -> %I32X", ui, MathSwap(ui));
const ulong ul = 0x0123456789ABCDEF;
PrintFormat("%I64X -> %I64X", ul, MathSwap(ul));
2
3
4
Đây là kết quả:
12345678 -> 78563412
123456789ABCDEF -> EFCDAB8967452301
2
Hãy thử ghi lại một mảng các byte sau khi chuyển đổi giá trị 0x12345678 bằng MathSwap
:
bo = MathSwap(ui); // đặt kết quả của MathSwap vào ByteOverlay
ArrayPrint(bo.bytes); // 18 52 86 120 <==> 0x12 0x34 0x56 0x78
2
Trong byte có chỉ số 0, nơi trước đây là 0x78, giờ đây là 0x12, và ở các phần tử có số khác, các giá trị cũng được hoán đổi.