От начального до среднего уровня: Объединение (I)
Введение
Представленные здесь материалы предназначены только для изучения. Ни в коем случае нельзя рассматривать это приложение как окончательное, цели которого будут иные, кроме изучения представленных концепций.
В предыдущей статье От начального до среднего уровня: Массив (IV), мы проработали очень интересную и полезную концепцию, которую многие считают продвинутой, и по моему скромному мнению, её должен знать и понимать каждый начинающий программист. Дело в том, что при правильном использовании данная концепция может буквально открыть целый мир возможностей, поскольку позволяет делать то, что иначе было бы очень сложно или почти невозможно.
Кроме того, данная же концепция используется и в других моментах, о которых мы еще поговорим. Чтобы избежать лишней тревожности, я вам дам один совет: старайтесь изучать и практиковать материалы, которые мы рассматривали в предыдущей статье. Очень важно, чтобы вы хорошо поняли и усвоили эти знания, потому что без них абсолютно всё, что вы увидите в дальнейшем, не будет иметь смысла, и всё, что мы рассмотрим, покажется вам чем-то магическим.
Возможно, с моей стороны было небольшим преувеличением утверждать, что вы ничего не поймете, не разобравшись в том, что было сделано в предыдущей статье. Да, признаюсь, что немного преувеличил. Однако это не умаляет того факта, что вышеупомянутая статья является самой важной из опубликованных на сегодняшний день. Это для тех, кто хочет стать настоящим программистом. И еще для тех, кто хочет реализовать на языке программирования всё, что угодно, ведь показанная в предыдущей статье концепция применима не только к языку MQL5, но и к любому языку, для которого характерно правильное использование вычислительных ресурсов.
Прежде чем мы начнем, нам нужно поговорить о том, что является обязательным условием для этой статьи. Хотя некоторые люди могут подумать, что я преувеличиваю, но на мой взгляд, если вы хотя бы поверхностно не поймете то, о чем говорилось в предыдущей статье, будет почти невозможно понять то, что сделаем здесь. Я не говорю, что вы не поймете, я не имею этого в виду. Я говорю лишь о том, что вам будет гораздо сложнее следовать тому, что мы объясним здесь.
Предыдущая статья стала как бы переломным моментом, когда, с одной стороны, мы получили весь базовый материал по программированию, а теперь перейдем к более продвинутому материалу. И, как следует из названия статьи, здесь мы поговорим об ОБЪЕДИНЕНИИ. Но имеется в виду не объединение в прямом смысле этого слова, а термин, встречающийся в некоторых языках, - ОБЪЕДИНЕНИЕ (UNION). И, как обычно, мы начнем новую тему, которая сделает процесс внедрения и написания кода намного веселее и приятнее.
Появление ОБЪЕДИНЕНИЯ
В предыдущей статье мы рассмотрели реализацию кода, показанного ниже.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #include "Tutorial\File 01.mqh" 05. //+------------------------------------------------------------------+ 06. void OnStart(void) 07. { 08. const uint ui = 0xCADA5169; 09. ushort us = 0x43BC; 10. uchar uc = B'01011101'; 11. 12. uchar Infos[], 13. counter = 0; 14. uint start, 15. number; 16. 17. PrintFormat("Translation personal.\n" + 18. "FUNCTION: [%s]\n" + 19. "ui => 0x%s\n" + 20. "us => 0x%s\n" + 21. "uc => B'%s'\n", 22. __FUNCTION__, 23. ValueToString(ui, FORMAT_HEX), 24. ValueToString(us, FORMAT_HEX), 25. ValueToString(uc, FORMAT_BINARY) 26. ); 27. 28. number = sizeof(ui) + 1; 29. start = Infos.Size(); 30. ArrayResize(Infos, start + number); 31. Infos[counter++] = sizeof(ui); 32. Infos[counter++] = (uchar)(ui >> 24); 33. Infos[counter++] = (uchar)(ui >> 16); 34. Infos[counter++] = (uchar)(ui >> 8); 35. Infos[counter++] = (uchar)(ui & 0xFF); 36. 37. number = sizeof(us) + 1; 38. start = Infos.Size(); 39. ArrayResize(Infos, start + number); 40. Infos[counter++] = sizeof(us); 41. Infos[counter++] = (uchar)(us >> 8); 42. Infos[counter++] = (uchar)(us & 0xFF); 43. 44. number = sizeof(uc) + 1; 45. start = Infos.Size(); 46. ArrayResize(Infos, start + number); 47. Infos[counter++] = sizeof(uc); 48. Infos[counter++] = (uc); 49. 50. Print("******************"); 51. PrintFormat("The Infos block contains %d bytes.", Infos.Size()); 52. ArrayPrint(Infos); 53. Print("******************"); 54. 55. Procedure(Infos); 56. 57. ArrayFree(Infos); 58. } 59. //+------------------------------------------------------------------+ 60. void Procedure(const uchar &arg[]) 61. { 62. Print("Translation personal.\n" + 63. "FUNCTION: ", __FUNCTION__); 64. 65. ulong value; 66. 67. for (uchar c = 0; c < arg.Size(); ) 68. { 69. value = 0; 70. for (uchar j = arg[c++], i = 0; (c < arg.Size()) && (i < j); i++, c++) 71. value = (value << 8) | arg[c]; 72. Print("0x", ValueToString(value, FORMAT_HEX), " B'", ValueToString(value, FORMAT_BINARY), "'"); 73. } 74. } 75. //+------------------------------------------------------------------+
Код 01
Выполнив код 01, мы получим в результате то, что показано на рисунке ниже.

Рисунок 01
На рисунке 01 мы видим, что значения, определенные в строках 8, 9 и 10, передаются в процедуру в строке 60. Однако, когда мы смотрим на тип данных, ожидаемых процедурой, то понимаем, что это не описанные значения, а массив. В этом нет никакого смысла, поскольку многие, глядя на процедуру в строке 60, ожидают увидеть там операции с массивом, но это не совсем то, что происходит. Мы имеем дело с транскрипцией или переводом значений массива, чтобы восстановить переданные значения.
Обратите внимание, что у нас нет возможности узнать, какое имя или тип переменной используется при вызове процедуры. Это связано с отсутствием ссылки на данную информацию. Всё, что нам известно, - это количество элементов и значение каждого элемента, присутствующего в каждой переменной.
Многие программисты (и даже очень опытные) считают такой подход совершенно неприемлемым, поскольку мы не можем связать значение с переменной в коде. Однако то, что такие профессионалы презирают данную практику, заставляет их игнорировать тот факт, что для центрального процессора имя переменной не имеет большого значения. Всё, что видит процессор, - это ряд чисел. и не более того. Он не знает имени переменной или константы, которую мы используем, - для него подобная информация совершенно не важна.
Данное моделирование, созданное в памяти, как показано в коде 01, выглядит примерно так:

Рисунок 02
Я подчеркну всё это здесь, так как, понятие, которое мы объясним, может быть довольно запутанным, особенно если мы посмотрим на другое, очень похожее понятие.
На изображении 02 показаны значения, выделенные на изображении 01, а также некоторые маркеры, которые на изображении 02 выделяются зеленым цветом и обозначают:
Здесь начинается новое значение, состоящее из множества элементов.
Синие прямоугольники представляют каждый блок элементов, т.е. если бы массив не использовался, а использовались дискретные переменные, то нам понадобилось бы шесть дискретных переменных, поскольку есть шесть синих прямоугольников на изображении 02. Однако, несмотря на это, у нас есть прямоугольники красного цвета. Они представляют каждый из элементов, находящихся в массиве. Поскольку есть десять элементов, у нас десять прямоугольников красного цвета, но их могло бы быть и пять, если бы каждый элемент состоял из двух значений.
Однако из-за фрагментации (на мой взгляд, это слишком сложная тема, чтобы рассматривать ее здесь и сейчас), мы используем минимальное значение для размера каждого элемента. Это позволяет нам избежать фрагментации, хотя и замедляет процесс декодирования информации. Данный процесс выполняется циклом в строке 70, хотя многие ошибочно полагают, что он выполняется циклом в строке 67. На самом деле, это цикл строки 70.
Далее. Я думаю, вам не было сложно понять эту часть, но я хочу, чтобы вы внимательно посмотрели на изображение 02, остановились и подумали: нет ли способа прочитать синие прямоугольники без необходимости проходить через красные прямоугольники? Или, иными словами: есть ли способ получить значения красных прямоугольников, просто взглянув на синий прямоугольник? Это значительно облегчит нам жизнь, поскольку ускорит процесс написания кода, который выполняется между строками 28 и 48, и упростит само декодирование, поскольку мы будем читать непосредственно содержимое синих прямоугольников, в отличие от того, что делается в коде 01, где мы разбираем каждый синий прямоугольник, чтобы получить элементы красного цвета, которые хранятся в массиве.
На самом деле, именно эта идея, выросшая из этой самой концепции, и породила то, что мы знаем как объединение. Когда мы используем объединение, мы создаем отсек памяти, чтобы разделить синие прямоугольники на блоки разных размеров. Фактически, размер каждого блока или элемента будет зависеть от цели и задач создаваемого кода или приложения. После создания объединение позволяет программисту управлять каждым элементом большого блока на совершенно индивидуальной основе. Данная концепция широко используется в коде на C и C++, где объединения помогают нам легко, спокойно и безопасно манипулировать целыми строками элементов.
Чтобы понять, как работает объединение, давайте начнем с самого простого, перед тем как снова обработать код 01. Далее давайте посмотрим, что происходит в коде, показанном чуть ниже.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. union un_01 07. { 08. ulong u64_bits; 09. uint u32_bits[2]; 10. }info; 11. 12. uint tmp; 13. 14. info.u64_bits = 0xA1B2C3D4E5F6789A; 15. 16. PrintFormat("Before modification: 0x%I64X", info.u64_bits); 17. 18. tmp = info.u32_bits[0]; 19. info.u32_bits[0] = info.u32_bits[1]; 20. info.u32_bits[1] = tmp; 21. 22. PrintFormat("After modification : 0x%I64X", info.u64_bits); 23. } 24. //+------------------------------------------------------------------+
Код 02
Посмотрите, как одно приводит к другому. Без понимания массивов (о которых говорилось выше), вы не поймете, что делается в этой точке кода. И, без понимания того, что мы сделали в предыдущей статье, показанное здесь не будет иметь никакого смысла. Сейчас самое время объяснить, что такое объединение и какова его практическая цель.
Когда вы запустите код 02, в терминале MetaTrader 5 появится такое изображение:

Рисунок 03
Это ключевой момент объединения. Обратите внимание, что это почти волшебство, это первый уровень того, что мы можем сделать. Но сначала давайте посмотрим, что происходит в коде 02. Во-первых, как видно из шестой строки, необходимо объявить объединение. Иными словами, за зарезервированным словом union следует другое слово, которое в данном случае является идентификатором объединения. Существует разница между тем, как объявляется объединение в MQL5 и в языках C и C++. В MQL5 НЕВОЗМОЖНО создать анонимное объединение. То есть данный идентификатор, который виден после слова union, должен ВСЕГДА существовать в MQL5.
Если его не существует, как это происходит в C и C++, у нас тогда получится так называемое анонимное объединение. Проблема с анонимным объединением заключается в том, что его нельзя использовать вне объявления. Мы это увидим в другом коде, но сейчас давайте сосредоточимся на коде 02. Как видите, объединение после объявления и идентификации начинается с открывающей фигурной скобки и заканчивается с закрывающей. Всё, что находится внутри данного блока, является частью объединения и делит с ним одну и ту же область памяти.
Теперь в игру вступает важная часть, которую мы наблюдаем уже некоторое время. Всё, что находится в памяти, должно рассматриваться в разрезе байтов. АБСОЛЮТНО ВСЁ. Поэтому, чтобы правильно создать объединение, необходимо продумать, как будем делить память на байты. Давайте вернемся к изображению 02. В нем отображаются 10 байт. То, как мы их сгруппируем, хотя это и не самое подходящее слово, поможет нам создать объединение. Рассматриваемое нами объединение, определенное в шестой строке, будет занимать восемь байт памяти, а его самым большим элементом будет как раз переменная u64_bits.
А как насчет переменной u32_bits, которая в данном случае является массивом? Не будет ли она занимать еще восемь байт, так как тип переменной говорит нам, что она занимает четыре? Так не будет ли правильным сказать, что объединение займет в общей сложности 16 байт? Нет, дорогой читатель, на самом деле, объединение будет занимать всего восемь байт. Однако и u64_bits, и u32_bits используют одну и ту же область памяти.
Я знаю, что поначалу это кажется очень запутанным. Так что давайте не будем торопиться, ведь если пропустить некоторые шаги в объяснении, всё станет еще запутаннее.
Цель кода 02 - выполнить обмен между одной частью памяти и другой частью, чтобы в конце концов информация, которая находится в памяти, была повернута. Для этого нам нужна временная переменная. В данном случае она объявлена в строке 12. Важный момент: данная временная переменная должна иметь размер в байтах, равный или больший размеру самой маленькой переменной, присутствующей в объединении. Как правило, мы используем один и тот же тип, который гарантирует правильный размер для достижения нашей цели.
Уже в строке 14 мы инициализируем объединение. Важно (и это должно быть ясно для вас), что фактическая переменная, имя которой относится к области памяти, объявляется в строке 10. Однако, поскольку мы не можем напрямую обратиться к этой области именно потому, что там может существовать больше переменных, мы должны сообщить компилятору имя переменной, к которой мы хотим получить доступ в области объединения. Мы можем использовать любой из присутствующих в этой области. Если всё делается правильно, компилятор поймет это и осуществит присвоение правильному значению, в результате чего область изменит значение.
Чтобы понять это, давайте вернемся к изображению 02. Возможно, вам кажется, что всё изображение - это объединение, и это действительно так. Но давайте рассмотрим более подробно. Теперь подумайте о том, что у каждого из прямоугольников красного цвета есть имя. Поэтому, если мы хотим получить доступ к любому из них, просто сообщим компилятору имя прямоугольника, и он присвоит или прочитает значение именно этого прямоугольника.
Здорово, не правда ли? Но не будем торопиться. Для начала необходимо разобраться с кодом 02. После того, как мы присвоили значение переменной u64_bits, вся область памяти под названием info содержит значение, указанное в строке 14.
Чтобы показать, что это действительно так, мы используем строку 16, чтобы показать содержимое памяти. Это выведет наше первое сообщение на терминал. Теперь самое интересное: поскольку мы хотим поменять местами значения позиций, чтобы создать новое значение в той же области памяти, мы можем использовать разделение памяти, предоставляемое объединением, чтобы сделать данный процесс простым и практичным.
Первое, что нужно сделать, - это использовать нашу временную переменную для хранения одного из значений. Сделаем это в строке 18. Затем, в строке 19, значение массива с индексом один мы присваиваем индексу ноль. Теперь наша память похожа на фруктовый салат: всё перемешано, но содержит лишь часть первоначального значения. В заключение, в строке 20 мы помещаем в индекс 1 значение, которое изначально находилось в индексе ноль. Таким образом, обмен завершился успешно, и, как видно на изображении 03, выполнение строки 22 показано в качестве второй строки.
Если вы думаете, что это безумие, давайте рассмотрим еще более интересный случай, в котором мы зеркально отображаем всё содержимое очень простым и практичным способом. Для этого мы перейдем к коду 03, показанному ниже.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. union un_01 07. { 08. ulong u64_bits; 09. uchar u8_bits[sizeof(ulong)]; 10. }; 11. 12. un_01 info; 13. 14. info.u64_bits = 0xA1B2C3D4E5F6789A; 15. 16. PrintFormat("The region is composed of %d bytes", sizeof(info)); 17. 18. PrintFormat("Before modification: 0x%I64X", info.u64_bits); 19. 20. for (uchar i = 0, j = sizeof(info) - 1, tmp; i < j; i++, j--) 21. { 22. tmp = info.u8_bits[i]; 23. info.u8_bits[i] = info.u8_bits[j]; 24. info.u8_bits[j] = tmp; 25. } 26. 27. PrintFormat("After modification : 0x%I64X", info.u64_bits); 28. } 29. //+------------------------------------------------------------------+
Код 03
Код 03 еще интереснее, чем предыдущий. И, запустив его, мы видим в терминале MetaTrader 5 изображение 04:

Рисунок 04
Дружище, вот это безумие! Тем не менее, это довольно круто и очень интересно. И здесь я пытаюсь сделать это с большим трудом, страдаю и изнуряю себя. А тут появляешься ты и показываешь нам, что мы можем сделать это очень просто. Это замечательно, правда, мне понравилось, но теперь я сомневаюсь в некоторых вопросах.
Давайте разберемся по порядку. Во-первых, что делается в девятой строке? Обычно мы используем массивы в объединениях, чтобы облегчить доступ к определенным точкам в общей памяти. Есть и другой способ сделать это без использования массивов, но об этом мы поговорим позже. Поскольку чаще всего нам нужен доступ ко всей области, чтобы правильно ею манипулировать, нередко используется что-то вроде того, что видно в девятой строке. В реальном коде, однако, данный способ объявления отличается еще больше. Но принцип и цель всегда одни и те же: создать возможность доступа ко всей области в рамках объединения, поэлементно.
И еще один вопрос: что это за странная штука, которая объявляется в строке 12? Для меня это не имеет смысла. Строка 12, - это как раз то, что делает объяснение, предоставленное по поводу кода 02, еще более уместным. Помните, мы говорили, что здесь, в MQL5, мы НЕ МОЖЕМ иметь анонимное объединение? Поскольку в шестой строке кода 03 мы объявляем объединение, мы можем использовать идентификатор un_01 как способ объявления переменной, которая будет его содержать. У нас может быть несколько переменных, содержащих разные объединения и имеющих один и тот же идентификатор. Это происходит так, потому что в момент объявления специального типа (а объединение является специальным типом), мы можем использовать один и тот же идентификатор в разных местах кода.
Единственный момент заключается в том, что идентификатор имеет тот же принцип видимости, что и любая переменная или константа. Об этом мы уже рассказывали в предыдущих статьях. Однако объединение, и это очень важно, ВСЕГДА будет переменной, оно никогда не может быть константой. Хотя мы можем записать его на константу, объединение всегда будет считаться переменной, но переменная специального типа очень похожа на строку.
По этой причине статьи публикуются таким образом, чтобы можно было понять некоторые концепции еще до того, как увидим применение других. Без понимания разницы между переменной и константой объяснить это будет сложно. Еще одним ключевым моментом является тот факт, что массив внутри объединения НИКОГДА НЕ БУДЕТ ДИНАМИЧЕСКОГО ТИПА. Оно всегда будет иметь статический тип.
Поэтому не стоит создавать огромное объединение с помощью динамического массива, потому что если мы попытаемся это сделать, компилятор не поймет, что мы пытаемся сделать.
Последний момент, на который вы, возможно, еще не обратили внимания, - это то, как цикл в строке 20 зеркально отображает содержимое области памяти. Чтобы «зеркальность» имела место, необходимо использовать счетчик, который достигнет половину области. Поскольку мы всегда используем четное количество элементов, это легко сделать. Данный цикл (показанный в строке 20) позволяет отразить любое значение дискретного типа, если мы внесем соответствующие изменения (не в цикле, а в заявлении блока, присутствующего на объединении шестой строки). Разумеется, необходимо также скорректировать значение, указанное в строке 14. В остальном, однако, никаких изменений в коде с нашей стороны не потребуется.
Например, если вы хотите зеркально отразить 32-битную переменную типа int, нам нужно будет только изменить объявления типов, используемые в строках восемь и девять, с ulong на int. Сделав это, мы сможем изменить значение, объявленное в строке 14, на нужное нам значение, и всё готово. Код уже сможет отражать тип int вместо типа ulong. Всё очень просто.
Однако на самом деле существует гораздо более простой способ сделать это. Но, я еще не объяснил другую концепцию, которую мы можем использовать в MQL5. Самый простой способ - сделать так, как описано выше.
Хорошо, прежде чем мы закончим, давайте посмотрим на последний момент, который мы можем реализовать с помощью объединений. И это связано с их использованием в функциях и процедурах. Чтобы продемонстрировать это, мы возьмем код 03 и изменим цикл, поместив его в функцию. Позже мы рассмотрим этот же процесс, только применительно к процедуре. В любом случае наша цель состоит в том, чтобы функция или процедура выполнила зеркальное отражение за нас, а затем отобразила результат в основной процедуре.
Конечно, существует несколько способов сделать это, но здесь мы будем использовать один из них в образовательных целях, поскольку наша цель состоит в том, чтобы показать, как мы можем использовать объединение в более широком смысле. Начнем с кода, который, на мой взгляд, легче понять, потому что мы будем использовать реализацию, очень похожую на ту, что показана в коде 03.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. union un_01 05. { 06. ulong u64_bits; 07. uchar u8_bits[sizeof(ulong)]; 08. }; 09. //+------------------------------------------------------------------+ 10. void OnStart(void) 11. { 12. un_01 info; 13. 14. info.u64_bits = 0xA1B2C3D4E5F6789A; 15. 16. PrintFormat("The region is composed of %d bytes", sizeof(info)); 17. 18. PrintFormat("Before modification: 0x%I64X", info.u64_bits); 19. 20. Swap(info); 21. 22. PrintFormat("After modification : 0x%I64X", info.u64_bits); 23. } 24. //+------------------------------------------------------------------+ 25. void Swap(un_01 &info) 26. { 27. for (uchar i = 0, j = sizeof(info) - 1, tmp; i < j; i++, j--) 28. { 29. tmp = info.u8_bits[i]; 30. info.u8_bits[i] = info.u8_bits[j]; 31. info.u8_bits[j] = tmp; 32. } 33. } 34. //+------------------------------------------------------------------+
Код 04
Код 04 довольно прост. Однако необходимо обратить внимание на некоторые моменты. Первый момент заключается в том, что объединение теперь не локальное, а глобальное. Но сделано это именно для того, чтобы дать процедуре, объявленной в строке 25, доступ к специальному типу, который мы создаем в строке 4. Если бы объединение не было объявлено глобальным, использование специального типа, который мы объявили как un_01, было бы невозможно в объявлении параметра в строке 25. Обратите внимание, что всё, что мы здесь сделали, - это перенесли код, который находился внутри основной процедуры внутрь процедуры. И вместо цикла в строке 20 кода 03 мы помещаем вызов нашей процедуры в ту же строку. По сути, мы обнародовали часть кода, которая ранее была приватной. Я искренне верю, что вы сможете без труда разобраться в коде 04. Однако сейчас мы рассмотрим другой случай, когда вместо процедуры мы будем использовать функцию. Это можно увидеть чуть ниже.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. union un_01 05. { 06. ulong u64_bits; 07. uchar u8_bits[sizeof(ulong)]; 08. }; 09. //+------------------------------------------------------------------+ 10. void OnStart(void) 11. { 12. un_01 info; 13. 14. info.u64_bits = 0xA1B2C3D4E5F6789A; 15. 16. PrintFormat("The region is composed of %d bytes", sizeof(info)); 17. 18. PrintFormat("Before modification: 0x%I64X", info.u64_bits); 19. 20. PrintFormat("After modification : 0x%I64X", Swap(info).u64_bits); 21. } 22. //+------------------------------------------------------------------+ 23. un_01 Swap(const un_01 &arg) 24. { 25. un_01 info = arg; 26. 27. for (uchar i = 0, j = sizeof(info) - 1, tmp; i < j; i++, j--) 28. { 29. tmp = info.u8_bits[i]; 30. info.u8_bits[i] = info.u8_bits[j]; 31. info.u8_bits[j] = tmp; 32. } 33. 34. return info; 35. } 36. //+------------------------------------------------------------------+
Код 05
Здесь всё немного сложнее, но только потому что для вас это является чем-то новым. Обратите внимание, что код очень похож на тот, который мы видели раньше, когда использовали процедуру. Однако, если посмотреть на одну и ту же строку 20 в трех последних кодах, то можно предположить, что код 05 может оказаться самым сложным для неопытных программистов. Но это не повод для тревоги, поскольку мы имеем дело с функцией, объявленной в строке 23. Для начала мы можем наблюдать кое-что интересное. Во-первых, в кодах 03 и 04 содержимое переменной, объявленной в строке 12, постоянно модифицируется. И это факт. Однако в коде 05 содержимое переменной, объявленной в строке 12, НЕ ИЗМЕНЯЕТСЯ.
Но подождите секунду, что значит "содержимое не изменяется", разве мы не обеспечили его зеркальность, чтобы получить результат, который видим на изображении 4? Дело не в этом, на самом деле зеркальное отражение работает. И для этих трех последних кодов результат будет таким, как показываем на рисунке 4. Но когда я говорю, что содержимое переменной 12 не изменяется, это связано с тем, что на самом деле оно не изменяется. Это вполне можно проверить, потому что в строке 23 мы передаем вещи как константную ссылку.
Теперь всё стало очень сложно. Ранее вы говорили, что мы не можем использовать объединение на постоянной основе, а теперь говорите, что можно? Друг, определись в конце концов. Ладно, возможно, я неудачно выразился. Или, возможно, вы путаете назначение с объявлением. Поскольку объявление выполняется в строке 23, оно является константой. Это предотвратит изменение переменной, присвоенной данной константе. Однако для работы нам нужна переменная. Поскольку объявление является константой, нам нужна строка 25 для создания новой переменной. Это можно изменить, и результат будет возвращен в строке 34.
Именно в этом месте многие могут запутаться. Но если Swap - это функция в коде 05, и мы возвращаем переменную, не следует ли присвоить возвращаемое значение другой переменной, чтобы только после этого мы могли использовать значение, которое вернул Swap? Это зависит от ситуации, дорогой читатель. Однако, поскольку функция - это тип переменной, о чем уже рассказывалось в другой статье, то нам можно использовать специальный тип, объявленный в четвертой строке, как способ прямого доступа к имеющимся там данным.
Это возможно только благодаря тому, что функция действительно возвращает данный конкретный тип. Если бы возвращаемое значение было дискретным, было бы невозможно реализовать то, что мы видим в строке 20, поэтому для достижения того же результата пришлось бы использовать другой тип ресурса. Однако, поскольку это потенциально запутанная тема, я оставлю ее для другого раза. Но есть способы сделать это.
Заключительные идеи
В данной статье мы рассмотрели, что такое объединение. Сегодня мы поэкспериментировали с первыми конструкциями, в которых можно использовать объединение. Тем не менее, то, что мы рассмотрели, является лишь основой целого набора концепций и информации, что будет рассмотрено в следующих статьях. Так что, дорогой читатель, вот мой золотой совет. Попрактикуйтесь и рассматривайте изученные материалы.
В приложении вы найдете основные коды, представленные в этой статье. А в следующей статье мы еще глубже погрузимся в основы программирования на MQL5. До скорой встречи!
Перевод с португальского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/pt/articles/15502
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Создание советника на MQL5 на основе стратегии Прорыва дневного диапазона (Daily Range Breakout)
Индикатор CAPM модели на рынке Forex
Возможности Мастера MQL5, которые вам нужно знать (Часть 44): Технический индикатор Average True Range (ATR)
От начального до среднего уровня: Массив (IV)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Não entendo nada! Gostaria muito que alguém iniciasse uma série de artigos "Do nível zero ao iniciante"! E aí você tem um iniciante com duas formações superiores em programação...
Но цель этой моей статьи именно в этом. Начать человека с полного нуля. Однако вы вошли в статью, где материал уже немного более продвинутый. Предлагаю вам начать с этой:
От базового до среднего: переменные (I)
Подробно: Все статьи с более продвинутым материалом имеют ссылку в начале, чтобы вы могли увидеть предыдущую статью. Но эту я предлагаю. На самом деле, она первая. Где я начинаю объяснять вещи с нуля. 🙂👍
Ни чего не понятно! Очень хотелось бы что бы кто нибудь начал серию статей "От нулевого уровня до начинающего"! А то у вас начинающий с 2мя высшими образованиями в программировании...
Так это ж перевод с португальского, и далеко не самый лучший.![😑]()
Гораздо понятнее будет то, что изначально написано на русском языке. Например, вот эта книга.