English Español Deutsch 日本語 Português
preview
Расширенные переменные и типы данных в MQL5

Расширенные переменные и типы данных в MQL5

MetaTrader 5Трейдинг | 24 июля 2024, 16:36
433 3
Mohamed Abdelmaaboud
Mohamed Abdelmaaboud

Введение

MQL5 — это язык программирования самой популярной торговой платформы MetaTrader 5, весьма богатой на инструменты создания торговой системы любой сложности. Наша цель как разработчиков — понять, как использовать эти инструменты и концепции для достижения цели разработки.

Мы уже упоминали основы простых переменных и типов данных в MQL5 в статье "Как и зачем разрабатывать собственную систему для алгоритмической торговли". Кроме того, мы рассмотрели использование MQL5 для написания торговых приложений, а также переменные с точки зрения их определения, типов и способов применения. Мы также узнали о простых типах данных, таких как integer, float, double, string и bool. Мы также упомянули входные переменные и то, как их можно использовать, чтобы дать пользователю возможность устанавливать свои предпочтения в программе.

В статье "Работаем с датами и временем в MQL5" мы подробно рассмотрели тип данных Datetime, без которого не обходится ни одно торговое приложение.

В этой статье мы рассмотрим новые переменные и типы данных в MQL5, а также их применение при разработке торгового программного обеспечения MQL5. Мы узнаем больше о некоторых продвинутых концепциях переменных и типов данных и рассмотрим их в следующих темах:

Мы подробно рассмотрим каждую тему, включая многочисленные примеры, чтобы углубить наше понимание и иметь возможность эффективно использовать все упомянутые концепции. Программирование — это наука, в которой очень важна практика, поэтому важно пробовать и применять полученные знания, чтобы понять, как можно использовать каждую концепцию как часть общего кода для создания эффективной торговой системы.


Константы

В этой части мы углубимся в концепцию констант в программировании и MQL5, чтобы понять, почему нам нужно использовать переменные этого типа. Постоянная переменная также называется доступной только для чтения или именованной константой. Ее нельзя изменить и мы не можем присвоить ей новое значение после первой инициализации. Если код попытается это сделать, это приведет к ошибке. Понятие константы поддерживается большинством языков программирования, включая MQL5. У нас есть два метода определения константы в нашем программном обеспечении:

  • Глобальные константы
  • Спецификатор const

Глобальные константы:

Эту глобальную константу можно глобально определить с помощью директивы препроцессора #define в начале нашей программы, затем указываем идентификатор, а после этого присваиваем значение константы. Значение константы может быть любым типом данных, например строковым, и в нашем программном обеспечении нам нужно закодировать следующее:

#define Identifier_Name constant_value

Как мы видим в предыдущем коде, у нас есть директива препроцессора #define, которая указывает на объявление константы. Следующий пример позволяет лучше понять этот метод:

В глобальной области видимости мы сначала используем директиву препроцессора #define, чтобы объявить, что у нас есть константа, идентификатор — PLATFORM_NAME, а постоянное значение идентификатора — строковый тип данных "MetaTrader 5".

#define PLATFORM_NAME "MetaTrader 5"

В нужной функции мы можем, например, сказать, что напечатаем идентификатор в OnStart()

void OnStart()
  {
   Print("The trading platform name is: ", PLATFORM_NAME);
  }

При компиляции и запуске программного обеспечения мы можем найти результат в виде сообщения на вкладке "Эксперты", как показано на следующем скриншоте.

constant_example

Как мы уже упоминали, значение константы MetaTrader 5 невозможно изменить.

Спецификатор const:

В соответствии с этим методом мы можем объявить константу, используя спецификатор const перед переменной и ее типом данных. Этот спецификатор объявляет, что это будет константа и ее нельзя изменить. Ниже показано, как мы можем это сделать.

const int varName = assignedVal

Ниже приведен простой пример объявления константы этим методом.

const int val = 1;

Если нам нужно напечатать значение этой переменной

Print("constant val is: ", val);

Мы можем найти результат, аналогичный следующему:

constant_example2

Как я уже упоминал, если мы попытаемся обновить переменную (val), то получим следующую ошибку, поскольку она является константой и не может быть обновлена.

constant_error

Чтобы проиллюстрировать разницу между обычной переменной и константой, мы можем использовать следующий пример, где у нас есть два значения: константа и обычная переменная.

   const int val = 1;
   int val2 = 2;
   
   Print("constant val is: ", val);
   Print("val 2 is: ", val2);
Результат при запуске программы представлен ниже.

diffExam

Теперь попытаемся обновить val2 другим значением, равным 4, с помощью этого кода.

val2 = 4;

Если мы затем снова распечатаем эти два значения, результат будет следующим:

diffExam2

Как видим, значение val2 меняет значение с 2 на 4.

Массивы

Массив - базовое понятие любого языка программирования. Массив — это переменная, в которой мы можем хранить множество значений любого типа данных. Мы можем представить массив в виде списка с индексами и соответствующими значениями. Когда нам нужно получить доступ к определенному значению, мы можем сделать это путем индексации.

Индексация массива начинается с нуля, поэтому максимальный индекс равен результату уменьшения размера массива на одно значение. Если у нас есть массив с пятью значениями, его индексы равны (0, 1, 2, 3, 4), а максимальное значение, как видим, равно 4, что является результатом (5-1).

Если нам нужно получить доступ к значению определенного индекса, мы ссылаемся на него, указывая его индекс в квадратных скобках []. Это то, что мы подразумеваем под доступом посредством индексации.

У нас также есть статический и динамический массивы. Разница между ними с точки зрения размера массива заключается в том, что статический массив имеет фиксированный размер и не может быть изменен, динамический массив не имеет размера. Его можно использовать, если нам нужно изменить размер массива вместо использования статического.

Объявление статического массива

Если нам нужно объявить новый статический массив, мы можем сделать это, используя следующий пример.

int newArray[5];

newArray[0] = 1;
newArray[1] = 2;
newArray[2] = 3;
newArray[3] = 4;
newArray[4] = 5;

Print("newArray - Index 0 - Value: ", newArray[0]);
Print("newArray - Index 1 - Value: ", newArray[1]);
Print("newArray - Index 2 - Value: ", newArray[2]);
Print("newArray - Index 3 - Value: ", newArray[3]);
Print("newArray - Index 4 - Value: ", newArray[4]);

После запуска программы мы увидим следующий результат.

staticArrayDeclaration

Мы можем получить тот же результат, объявив массив с использованием следующего сокращенного кода для большей эффективности, а также присвоив значения массиву внутри скобок {} и разделив их запятой (,).

   int newArray[5] = {1, 2, 3, 4, 5};

   Print("newArray - Index 0 - Value: ", newArray[0]);
   Print("newArray - Index 1 - Value: ", newArray[1]);
   Print("newArray - Index 2 - Value: ", newArray[2]);
   Print("newArray - Index 3 - Value: ", newArray[3]);
   Print("newArray - Index 4 - Value: ", newArray[4]);

 Если мы выполним данный код, мы получим уже показанные выше сообщения.

Объявление динамического массива

Как уже упоминалось, динамический массив — это массив без фиксированного размера, размер которого можно изменить. Этот тип массива используется для хранения таких данных, как цена и значение индикатора, поскольку эти данные являются динамическими.

Ниже показано, как можно объявить новый динамический массив.

   double myDynArray[];
   ArrayResize(myDynArray,3);

   myDynArray[0] = 1.5;
   myDynArray[1] = 2.5;
   myDynArray[2] = 3.5;
   
   Print("Dynamic Array 0: ",myDynArray[0]);
   Print("Dynamic Array 1: ",myDynArray[1]);
   Print("Dynamic Array 2: ",myDynArray[2]);

Как мы видим в предыдущем коде, мы объявили массив с пустой квадратной скобкой, затем использовали функцию ArrayResize для изменения размера массива. Результат следующий.

dynamicArrayDeclaration

У нас есть три напечатанных сообщения для каждого индекса и соответствующее значение. Мы определили одномерные массивы, но мы также можем использовать многомерные массивы - два, три или четыре.

Многомерные массивы

Многомерные массивы можно рассматривать как вложенные массивы или массивы внутри массивов. Например, у нас есть два массива, каждый из которых имеет два элемента. Посмотрим, как мы можем закодировать этот тип массива следующим образом:

Объявление двумерных массивов с двумя элементами

double newMultiArray[2][2];

Присвоение значений или элементов первому массиву с индексом 0

newMultiArray[0][0] = 1.5;
newMultiArray[0][1] = 2.5;

 Присвоение значений или элементов второго массива с индексом 1

newMultiArray[1][0] = 3.5;
newMultiArray[1][1] = 4.5;

Печать значений двух массивов и их элементов или значений

   Print("Array1 - Index 0 - Value: ", newMultiArray[0][0]);
   Print("Array1 - Index 1 - Value: ", newMultiArray[0][1]);
   Print("Array2 - Index 0 - Value: ", newMultiArray[1][0]);
   Print("Array2 - Index 1 - Value: ", newMultiArray[1][1]);

После запуска программы мы увидим следующий результат.

multiDymArrays

Как видим, у нас есть массив 1 с двумя значениями 1.5 и 2.5, а также Массив 2 с двумя значениями 3.5 и 4.5.

Когда мы объявляем многомерные массивы, мы можем оставить пустым только первое измерение, как в следующем примере.

double newMultiArray[][2];

Затем мы можем передать его, используя переменную, аналогичную следующей:

int array1 = 2;
ArrayResize(newMultiArray, array1);

После того, как мы скомпилировали и запустили приложение, мы получим тот же результат, что и раньше.


Перечисления

Мы можем представить перечисления как наборы данных или списки констант/элементов, которые можно использовать для описания связанных понятий. Перечисления могут встроенными и пользовательскими. Встроенные перечисления предопределены в MQL5, и мы можем вызывать и использовать их в нашей программе. Пользовательские перечисления вводятся в соответствии с нашими потребностями.

Встроенные перечисления можно найти в справочнике MQL5, например, ENUM_DAY_OF_WEEK. Мы также можем установить пользовательские перечисления, чтобы использовать их позже в созданных нами приложениях.

Прежде всего мы используем ключевое слово enum для определения перечисления, имя типа перечисления и список значений связанных типов данных, разделенных запятыми.

enum name of enumerable type 
  { 
   value 1,
   value 2,
   value 3
  };

Например, создадим именованное перечисление workingDays, которое будет типом. Переменные для него я объявлю позже

   enum workingDays
     {
      Monday,
      Tuesday,
      Wednesday,
      Thursday,
      Friday,
     };

Теперь нам нужно объявить новую связанную переменную типа workingDays, присвоить сегодняшнее значение (today) среди определенных в списке и распечатать эту переменную.

     workingDays toDay;
     toDay = Wednesday;
     
     Print(toDay);

Мы можем найти сообщение (2) для среды (Wednesday) как рабочего дня в списке, начиная с 0 для понедельника (Monday) и до 4 для пятницы (Friday), как показано на следующем скриншоте.

enums

Мы также можем определить другой начальный номер, который будет назначен вместо (0), присвоив значению необходимый номер, например, указав, что понедельник = 1.


Структуры

Нам нужно объявить несколько различных типов данных для связанных переменных, и в этом случае нам поможет структура. Члены структуры могут иметь любой тип данных, в отличие от перечисления, члены которого могут быть только одного типа. Аналогично перечислениям, в MQL5 есть предопределенные структуры, такие как, например, MqlTick, но мы можем создавать свои собственные.

Допустим, нам нужно создать структуру для tradeInfo. В качестве членов у нас есть символ, цена, стоп-лосс, тейк-профит и время торговли. Они имеют разные типы данных: символ представляет собой строку, а цена, стоп-лосс и тейк-профит — значение типа double. В этом случае нам нужно использовать структуры для выполнения нашей задачи:

Используем ключевое слово struct, чтобы объявить нашу собственную структуру.

   struct tradeInfo
     {
      string         symbol;
      double         price;
      double         stopLoss;
      double         takeProfit;
     };

Затем мы можем объявить новый торговый объект типа tradeInfo и присвоить значения членам этой структуры, обратившись к ним с помощью (.) после имени объекта следующим образом

   tradeInfo trade;

   trade.symbol = "EURUSD";
   trade.price = 1.07550;
   trade.stopLoss = 1.07500;
   trade.takeProfit = 1.07700;

Мы можем распечатать члены структуры с их значениями, как показано ниже

   Print("Symbol Trade Info: ", trade.symbol);
   Print("Price Trade Info: ", trade.price);
   Print("SL Trade Info: ", trade.stopLoss);
   Print("TP Trade Info: ", trade.takeProfit);

Выведем сообщение, как на следующем скриншоте.

struct

Как можно видеть на предыдущем снимке экрана, именно такие назначенные значения нам и нужны, поэтому мы можем поэкспериментировать с ними для достижения наших целей.


Приведение типов

Под приведением типов понимается преобразование значение переменной из одного типа данных в другой. Этот процесс может привести к неожиданным результатам.

Когда мы приводим или копируем числовое значение из одной переменной в другую, мы можем столкнуться с потерей данных, когда этот процесс происходит между разными типами.

Ниже приведены примеры, когда данные не потеряны или потеряна часть данных:

Мы копируем целочисленное значение в значение long. В этом случае потери данных не произойдет, поскольку приведение выполняется от меньших переменных к большим. Когда мы копируем целое число в double, также копируется целое число и значение после десятичной точки равна нулю, поэтому нет данных, которые можно было бы потерять. Если же мы, наоборот, скопируем значение double в целочисленную переменную, значение после десятичной дроби будет потеряно (усечено).

Давайте рассмотрим примеры кода. Сначала мы увидим приведение данных без потерь:

   int price = 3 - 1;
   double newPrice = price + 1;
   
   Print ("The price is ", newPrice);

Как видим, тип переменной (цена) — int, затем мы приводим ее к переменной double и печатаем ее. Предполагается, что потери данных не возникнет, поскольку мы выполняем приведение от меньшего типа к большему. Ниже приводится отображаемое сообщение.

typeCasting1

Но когда мы производим обратную операцию, мы увидим совсем другую картину.

   double price = 3.50 - 1;
   int newPrice = price + 1;
   
   Print ("The price is ", newPrice);

Тип значения - double и значение цены равно 3.50. Приведем его к переменной int (newPrice) и распечатаем. Мы теряем данные после десятичной точки, которая является значением (.50), и результатом будет (3).

typeCasting2

Компилятор предупредит нас, если мы приведем значение большего типа к переменной меньшего типа. Мы сами решаем, игнорировать потерю данных или нет, в зависимости от наших целей и важности потерянных данных. Ниже приведен пример этого предупреждения:

typeCasting2

Мы можем округлить значение, добавив (int) перед переменной double, чтобы отобразить предупреждение компилятора.


Локальные переменные

Представим, что в нашем программном обеспечении есть глобальная и локальная области видимости. Глобальная область видимости заключается в том, что мы можем получить доступ к любому месту в нашем приложении (мы рассмотрим понятие глобальной области видимости с точки зрения переменных в следующем разделе), но локальная область может быть доступна только в той области видимости, в которой она объявлена.

Если у нас есть функция и внутри этой функции есть переменные, которые объявляются только на уровне функции, эти переменные считаются локальными. Таким образом, они могут быть доступны во время работы функции, но не будут доступны после выхода из функции.

Для наглядности рассмотрим следующий пример:

void myFunc()
{
   int var = 5;
   Print(var);
}

Как видим, у нас есть функция myFunc. Эту функцию можно вызывать где угодно. При вызове этой функции, мы видим локальную переменную var. Эта переменная может работать только внутри функции, но после выхода из функции она недоступна.

Итак, при вызове функции мы видим, что результат равен 5:

localVar

Если мы попытаемся получить доступ к локальной переменной var извне функции, компилятор выдаст ошибку undeclared identifier (необъявленный идентификатор):

localVarError

То же самое происходит, если внутри функции есть вложенные уровни или области видимости: каждая объявленная переменная будет доступна только в своей области видимости.

Рассмотрим это на следующем примере:

void myFunc()
  {
   int var = 5;
   if(var == 5)
     {
      int newVar = 6 ;
      Print(newVar);
     }

  }

Как видим, мы вложили if для сравнения значения var с 5. При true мы объявим newVar со значением 6 и распечатаем его. В результате мы получаем 6, поскольку условие истинно.

localVar2

newVar представляет собой новую локальную переменную внутри области действия оператора if. Она недоступна за ее пределами, как и любая другая локальная область видимости. Любое объявление переменной с тем же именем, что и у локальной переменной, переопределит предыдущее и будет введено в более высокую область видимости.

Итак, концепция очень проста: любая локальная переменная будет доступна на своем уровне или в области видимости внутри функции. Но что нам делать, если нам нужно получить доступ к переменной в любом месте нашего приложения? Здесь в игру вступает глобальная переменная.


Глобальные переменные

В отличие от локальных переменных, мы можем получить доступ к глобальным переменным из любого места нашего приложения. Когда нам нужно объявить глобальные переменные, это нужно делать в глобальной области видимости программного обеспечения вне какой-либо функции, чтобы мы могли использовать или вызывать их в любом месте приложения. Эти типы переменных можно использовать, когда нам нужно объявить переменную, которая будет многократно использоваться многими функциями.

Глобальные переменные определяются в начальной части приложения после входных переменных. В отличие от локальных переменных, если мы попытаемся объявить глобальные переменные внутри блока, это приведет к ошибке компиляции "Declaration of variable hides global declaration" (объявление переменной скрывает глобальное объявление):

globalVar

Мы можем обновлять глобальные переменные в любой точке приложения, как в следующем примере:

int stopLoss = 100;

void OnStart()
  {
   Print("1st Value: ", stopLoss);
   addToSL();
  }

void addToSL()
  {
   stopLoss = stopLoss + 50;
   Print("Updated Value: ", stopLoss);
  }

Как видим, мы объявили переменную stopLoss глобально в верхней части программы, распечатали назначенное значение, вызвали функцию addToSL(), которая добавила 50 к первому назначенному значению, а затем распечатали обновленное значение. После компиляции и запуска приложения мы увидим следующие сообщения:

globalVar2

Как видим, наше первоначальное значение переменной равно 100, а при обновлении путем добавления 50 в функции addToSL() оно становится равным 150.


Статические переменные

Статические переменные являются локальными переменными, но они сохраняют свои значения в памяти, даже если приложение покинуло их область видимости. Их можно объявить в блоке функций или локальных переменных.

Мы можем сделать это, используя ключевое слово static перед именем переменной и присвоив ее значение.

void staticFunc()
{
   static int statVar = 5;
   statVar ++;
   Print(statVar);
}

Вызовем функцию staticFunc()

staticFunc();

Здесь переменная сохранит свое значение (5) в памяти, и каждый раз, когда мы вызываем функцию, на выходе будет 6 (5+1). Ниже приведен скриншот вывода:

staticVar

Мы можем видеть значение 6 на вкладке "Эксперты".


Предопределенные переменные

При создании кода нам может потребоваться постоянно писать много строк, чтобы сделать что-то часто используемое. Во многих языках программирования есть предопределенные переменные или даже функции. Мы можем легко их использовать без необходимости заново переписывать весь код. Нам нужно лишь знать, какое ключевое слово нужно использовать.

В MQL5 имеется множество предопределенных переменных, и мы можем получить доступ к их значениям, как в примерах ниже:

  • _Symbol - текущий символ графика.
  • _Point - значению пункта текущего символа: 0,00001 для пяти цифр текущего символа и 0,001 для трех цифр текущего символа.
  • _Period - текущий период или таймфрейм символа.
  • _Digits - количество знаков после запятой (пять или три).
  • _LastError - значение последней ошибки.
  • _RandomSeed - текущее состояние генератора псевдослучайных чисел.
  • _AppliedTo - тип данных для расчета индикатора.

Эти и другие предопределенные переменные можно найти в одноименном разделе документации.


Заключение

Мы рассмотрели тонкости MQL5, уделив особое внимание основным понятиям программирования, таким как типы данных, переменные и другие ключевые элементы, имеющие решающее значение для разработки современных приложений. Овладение MQL5 требует понимания как базовых, так и продвинутых аспектов, при этом практика имеет основополагающее значение для повышения уровня профессионализма. Вы можете прочитать другие мои статьи, если хотите узнать больше о программировании или разработке торговых систем на основе таких популярных технических индикаторов, как скользящие средние, RSI и других.


Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/14186

Последние комментарии | Перейти к обсуждению на форуме трейдеров (3)
Dominik Egert
Dominik Egert | 14 мар. 2024 в 19:28
MetaQuotes:

Ознакомьтесь с новой статьей: Расширенные переменные и типы данных в MQL5.

Автор: Мохамед Абдельмаабуд


Я в некоторой степени не согласен с тем, что директива препроцессора может быть упомянута в контексте переменных const.

Ключевое слово const имеет другой эффект, чем #define "константа".

const может быть очень полезно в сигнатурах функций, поскольку оно переводит значение параметров в режим "только чтение". const - это ключевое слово, специально созданное для кодера. Оно не имеет представления в исполняемом двоичном файле, создаваемом компилятором.

В отличие от переменной const, оператор #define представляет собой так называемое r-значение, которое фактически находится в области памяти только для чтения в дальнейшем исполняемом двоичном файле, и поэтому фактически не может быть изменено каким-либо образом.

Кроме того, #define - это директива стадии препроцессора, и на самом деле она просто заменяет все вхождения в исходный файл. Это делается еще до того, как компилятор увидит компилируемый код.

Кроме того, директива #define не имеет такого "местоположения", как глобальная, она может появиться в любом месте исходного файла. На этапе препроцессора понятие кодовых блоков "{...}" не доступно и не оценивается.

Видимость переменных определяется кодовыми блоками. Вы можете на любом уровне определять кодовые блоки как угодно, кроме глобального уровня. На глобальном уровне вам нужна некоторая "сущность", к которой принадлежит этот блок. Функция, класс, структура или пространство имен.

Внутри такого "именованного" блока вы можете каскадировать сколько угодно блоков, при этом видимость всегда будет только внутри блоков того же уровня или включенных/дочерних блоков.

статические переменные фактически находятся в глобальном пространстве памяти, выделенном для программы, видимость этой переменной определяется блоком кода, в котором она была объявлена/определена. Это может быть и глобальное пространство. На самом деле, переменная, объявленная/определенная в глобальном пространстве, имеет неявное ключевое слово "static". Его явное упоминание никак не изменит поведение этой переменной.

В вашей статье мне не хватает ключевого слова "extern", а также "input". Я думаю, они должны быть частью статьи.

В любом случае, я думаю, что это хорошая статья, особенно для новичков, так как концепцию переменных иногда трудно понять в начале, и многих ошибок можно избежать, если правильно ее реализовать.

Возможно, вы продолжите статью, было бы неплохо, если бы вы рассказали об определении, объявлении и инициализации переменных, а также о памяти. Возможно, вы также укажете на подводные камни, если сделать это неправильно.

И для большего понимания, возможно, некоторые расширенные детали о стеке, порядок переменных, MSB против LSB, адресация памяти..... Ладно, может быть, это слишком далеко.
Mohamed Abdelmaaboud
Mohamed Abdelmaaboud | 27 июл. 2024 в 15:09
Dominik Egert #:

Я в некоторой степени не согласен с тем, что директива препроцессора может быть упомянута в контексте переменных const.

Ключевое слово const имеет другой эффект, чем #define "константа".

const может быть очень полезно в сигнатурах функций, поскольку оно переводит значение параметров в режим "только чтение". const - это ключевое слово, специально созданное для кодера. Оно не имеет представления в исполняемом двоичном файле, создаваемом компилятором.

В отличие от переменной const, оператор #define представляет собой так называемое r-значение, которое фактически находится в области памяти только для чтения в дальнейшем исполняемом двоичном файле, и поэтому фактически не может быть изменено каким-либо образом.

Кроме того, #define - это директива стадии препроцессора, и на самом деле она просто заменяет все вхождения в исходный файл. Это делается еще до того, как компилятор увидит компилируемый код.

Кроме того, директива #define не имеет такого "местоположения", как глобальная, она может появиться в любом месте исходного файла. На этапе препроцессора понятие кодовых блоков "{...}" не доступно и не оценивается.

Видимость переменных определяется кодовыми блоками. Вы можете на любом уровне определять кодовые блоки как угодно, кроме глобального уровня. На глобальном уровне вам нужна некоторая "сущность", к которой принадлежит этот блок. Функция, класс, структура или пространство имен.

Внутри такого "именованного" блока вы можете каскадировать сколько угодно блоков, при этом видимость всегда будет только внутри блоков того же уровня или включенных/дочерних блоков.

статические переменные фактически находятся в глобальном пространстве памяти, выделенном для программы, видимость этой переменной определяется блоком кода, в котором она была объявлена/определена. Это может быть и глобальное пространство. На самом деле, переменная, объявленная/определенная в глобальном пространстве, имеет неявное ключевое слово "static". Его явное упоминание никак не изменит поведение этой переменной.

В вашей статье мне не хватает ключевого слова "extern", а также "input". Я думаю, они должны быть частью статьи.

В любом случае, я думаю, что это хорошая статья, особенно для новичков, так как концепцию переменных иногда трудно понять в начале, и многих ошибок можно избежать, если правильно ее реализовать.

Возможно, вы продолжите статью, было бы неплохо, если бы вы рассказали об определении, объявлении и инициализации переменных, а также о памяти. Возможно, вы также укажете на подводные камни, если сделать это неправильно.

И для большего понимания, возможно, некоторые расширенные детали о стеке, порядок переменных, MSB против LSB, адресация памяти..... Ладно, может быть, это слишком далеко.


Спасибо, что поделились информацией. Я постараюсь написать о том, что вы упомянули, как можно больше.

Vladislav Boyko
Vladislav Boyko | 27 июл. 2024 в 22:05
Dominik Egert #:
Ключевое слово const действует иначе, чем #define "constant".

Кстати, мне нравится, как реализованы константы в C# (компилятор заменяет их на литеральные значения).

https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/constants

На самом деле, когда компилятор встречает идентификатор константы в исходном коде C#, он подставляет буквенное значение непосредственно в код промежуточного языка (IL), который он производит. Поскольку во время выполнения с константой не ассоциируется адрес переменной, const-поля не могут передаваться по ссылке и не могут выступать в качестве l-значения в выражении.

Судя по тому, что в MQL я могу передавать константу по ссылке, в MQL константы остаются переменными после компиляции.
Мониторинг торговли с помощью Push-уведомлений — пример сервиса в MetaTrader 5 Мониторинг торговли с помощью Push-уведомлений — пример сервиса в MetaTrader 5
В статье рассмотрим создание программы сервиса для отправки уведомлений на смартфон о результатах торговли. В рамках статьи научимся работать со списками объектов Стандартной Библиотеки для организации выборки объектов по требуемым свойствам.
Алгоритм адаптивного социального поведения — Adaptive Social Behavior Optimization (ASBO): Двухфазная эволюция Алгоритм адаптивного социального поведения — Adaptive Social Behavior Optimization (ASBO): Двухфазная эволюция
Эта статья является продолжением темы социального поведения живых организмов и его воздействия на разработку новой математической модели - ASBO (Adaptive Social Behavior Optimization). Мы погрузимся в двухфазную эволюцию, проведем тестирование алгоритма и сделаем выводы. Подобно тому, как в природе группа живых организмов объединяет свои усилия для выживания, ASBO использует принципы коллективного поведения для решения сложных задач оптимизации.
Введение в MQL5 (Часть 5): Функции для работы с массивами для начинающих Введение в MQL5 (Часть 5): Функции для работы с массивами для начинающих
В пятой статье из нашей серии мы познакомимся с миром массивов в MQL5. Статья предназначена для начинающих. В статье попытаемся упрощенно рассмотреть сложные концепции программирования, чтобы материал был понятен всем. Давайте вместе будем изучать основные концепции, обсуждать вопросы и делиться знаниями!
Разработка системы репликации (Часть 42): Проект Chart Trade (I) Разработка системы репликации (Часть 42): Проект Chart Trade (I)
Давайте создадим что-нибудь поинтереснее. Не хочу портить сюрприз, поэтому следите за статьей, чтобы лучше понять. С самого начала этой серии о разработке системы репликации/моделирования, я говорил, что идея состоит в том, чтобы использовать платформу MetaTrader 5 одинаково как в разрабатываемой нами системе, так и на реальном рынке. Важно, чтобы это было сделано должным образом. Никто не хочет тренироваться и учиться сражаться, используя одни инструменты, в то время как во время боя ему придется пользоваться другими.