Типові помилки в програмах на MQL4 та методи їх усунення.
При використанні нової версії компілятора MQL4 деякі старі програми можуть видавати помилки.
У старій версії компілятора щоб уникнути критичного завершення програм багато помилок оброблялися середовищем виконання і призводили до зупинки роботи. Наприклад, розподіл на нуль або вихід за межі масиву є критичними помилками і зазвичай призводять до аварійного завершення. Виявляються такі помилки лише в деяких станах при певних значеннях змінних, проте такі ситуації слід знати і коректно їх обробляти.
Новий компілятор дозволяє виявити реальні чи потенційні джерела помилок та підвищити якість коду.
У цій статті ми розглянемо можливі помилки, що виникають при компіляції старих програм, та методи їх усунення.
1. Помилки компіляції
За наявності помилок у коді програма не може бути скомпільована.
Для повного контролю всіх помилок рекомендується використовувати суворий компіляційний режим, який встановлюється директивою:
Цей режим значно спрощує пошук помилок.
1.1. Ідентифікатор збігається із зарезервованим словом
Якщо найменування змінної або функції збігаються з одним із зарезервованих слів:
то компілятор виводить повідомлення про помилки:
Рис.1. Помилки "unexpected token" та "name expected"
Для виправлення цієї помилки потрібно виправити ім'я змінної або функції.
1.2. Спеціальні символи у найменуваннях змінних та функцій
Якщо найменування змінних або функцій містять спеціальні символи ($, @, точка):
то компілятор виводить повідомлення про помилки:

Рис.2.Помилки "unknown symbol" та "semicolon expected"
Щоб виправити цю помилку, потрібно скоригувати імена змінних або функцій.
1.3. Помилки використання оператора switch
Стара версія компілятора дозволяла використовувати будь-які значення у виразах та константах оператора switch:
У новому компіляторі вирази та константи оператора switch повинні бути цілими числами, тому при використанні таких конструкцій виникають помилки:
Рис.3. Помилки "illegal switch expression type" та "constant expression is not integral"
У таких випадках можна використовувати явні порівняння чисельних значень, наприклад:
1.4. Значення функцій, що повертаються
Усі функції, крім void, мають повертати значення оголошеного типу. Наприклад:
При строгому режимі компіляції (strict) виникає помилка:
Рис.4. Помилка "no control control paths return a value"
У режимі компіляції за промовчанням компілятор виводить попередження:
Рис.5. Попередження "not all control paths return a value"
то при строгому режимі компіляції виникає помилка:
Рис.6. Помилка "function must return a value"
У режимі компіляції за промовчанням компілятор виводить попередження:
Рис.7. Попередження 'return - function must return a value"
Для виправлення таких помилок у код функції потрібно додати оператор повернення return c значенням відповідного типу, що повертається.
1.5. Масиви в аргументах функций
Масиви в аргументах функцій тепер передаються лише за посиланням.
Цей код при строгому режимі компіляції (strict) призведе до помилки:
Рис.8. Помилка компілятора "arrays passed byreference only"
У режимі компіляції за промовчанням компілятор виводить попередження:
Рис.9. Попередження компілятора "arrays passed by reference only"
Для виправлення таких помилок потрібно вказати передачу масиву за посиланням, додавши префікс & перед ім'ям масиву:
Слід зазначити, що тепер константні масиви (Time[], Open[], High[], Low[], Close[], Volume[]) неможливо знайти за посиланням. Наприклад, виклик:
незалежно від режиму компіляції призводить до помилки:
Рис.10. Помилка 'Open' - constant variable cannot be passed as reference
Для усунення подібних помилок потрібно скопіювати необхідні дані з константного масиву:
2. Помилки часу виконання
Помилки, які у процесі виконання коду програми прийнято називати помилками часу виконання (runtime errors). Такі помилки зазвичай залежить від стану програми пов'язані з некоректними значеннями змінних.
Наприклад, якщо змінна використовується як індекс елементів масиву, то її негативні значення неминуче призведуть до виходу за межі масиву.
2.1. Вихід межі масиву (Array out of range)
Ця помилка часто виникає в індикаторах під час звернення до індикаторних буферів. Функція IndicatorCounted() повертає кількість барів, які не змінилися після останнього виклику індикатора. Значення індикаторів на вже розрахованих раніше барах не потребують перерахунку, тому для прискорення розрахунків достатньо обробляти лише кілька останніх барів.
Більшість індикаторів, у яких використовується даний спосіб оптимізації обчислень, мають вигляд:
Часто зустрічається некоректне оброблення випадку counted_bars==0 (початкову позицію limitпотрібно зменшити на значення, що дорівнює 1 + максимальний індекс щодо змінної циклу).
2.2. Поділ на нуль (Zero divide)
Помилка "Zero divide" виникає у випадку, якщо при виконанні операції поділу дільник виявляється нульовим:
При виконанні даного скрипту у вкладці "Експерти" виникає повідомлення про помилку та завершення роботи програми:

Рис.11. Повідомлення про помилку "zero divide"
Зазвичай така помилка виникає у випадках, коли значення дільника визначається значеннями будь-яких зовнішніх даних. Наприклад, якщо аналізуються параметри торгівлі, то величина задіяної маржі виявляється дорівнює 0, якщо немає відкритих ордерів. Інший приклад: якщо аналізовані дані зчитуються з файлу, у разі його відсутності не можна гарантувати коректну роботу. З цієї причини бажано намагатися враховувати подібні випадки та коректно їх обробляти.
Найпростіший спосіб - перевіряти дільник перед операцією поділу та виводити повідомлення про некоректне значення параметра:
Внаслідок критичної помилки не виникає, але виводиться повідомлення про некоректне значення параметра і робота завершується:

Мал. 12. Повідомлення про некоректне значення дільника
2.3. Використання 0 замість NULL для поточного символу
У старій версії компілятора допускалося використання 0 (нуля) як аргумент у функціях, які потребують вказівки фінансового інструмента.
Наприклад, значення технічного індикатора Moving Average для поточного символу можна було вимагати наступним чином:
У новому компіляторі для вказівки поточного символу слід явно вказувати NULL:
Крім того, поточний символ та період графіка можна вказати за допомогою функційSymbol() та Period().
2.4. Рядки у форматі Unicodе та їх використання в DLL
Рядки тепер є послідовністю символів Unicode.
Зверніть увагу на цей факт і використовуйте відповідні функції Windows. Наприклад, при використанні функцій бібліотеки wininet.dll замість InternetOpenA() та InternetOpenUrlA() слід викликати InternetOpenW() та InternetOpenUrlW().
У MQL4 змінилася внутрішня структура рядків (тепер вона займає 12 байт), тому під час передачі рядків у DLL слід використовувати структуру MqlString:
2.5. Спільне використання файлів
У новому MQL4 при відкритті файлів необхідно явно вказувати прапори FILE_SHARE_WRITE та FILE_SHARE_READ для спільного використання.
У разі їх відсутності файл буде відкритий у монопольному режимі, що не дозволить більше нікому його відкривати, доки він не буде закритий монополістом.
Наприклад, під час роботи з офлайновими графіками потрібно явно вказувати прапори спільного доступу:
Подробиці можна знайти у статті у статті "Оффлайнові графіки та новий MQL4".
2.6. Особливість перетворення datetime
Слід пам'ятати, що перетворення типу datetime в рядок тепер залежить від режиму компіляції:
Наприклад, спроба роботи з файлами, ім'я яких містить двокрапку, призведе до помилки.
3. Попередження компілятора
Попередження компілятора мають інформаційний характер і не є повідомленнями про помилки, однак вони вказують на можливі джерела помилок і краще їх скоригувати.
Чистий код не повинен містити попередження.
3.1. Перетинання імен глобальних та локальних змінних
Якщо на глобальному та локальному рівнях присутнізмінні з однаковими іменами:
то компілятор виводить попередження і вкаже номер рядка, на якому оголошено глобальну змінну:
Рис.13. Попередження "declaration of '%' hides global declaration at line %"
Для виправлення таких попереджень потрібно скоригувати імена глобальних змінних.
3.2. Невідповідність типів
У новій версії компілятора введена операція приведення типів.
У строгому режимі компіляції за невідповідності типів компілятор виводить попередження:

Рис.14. Попередження "possible loss of data due to type conversion" та "implicit conversion from 'number' to 'string'
У цьому прикладі компілятор попереджає про можливу втрату точності при присвоєнні різних типів даних і неявне перетворення типу int в string.
Для виправлення потрібно використовувати явне наведення типів:
3.3. Незмінні змінні
Наявність змінних, які не використовуються в коді програми (зайві сутності) не є добрим тоном.
Повідомлення про такі змінні виводяться незалежно від режиму компіляції:

Рис.15. Попередження "variable '%' not used'
Для виправлення потрібно прибрати змінні з коду програми, що не використовуються.
У статті розглянуто типові проблеми, з якими можуть зіткнутися програмісти при компіляції старих програм, що містять помилки.
У всіх випадках при налагодженні програм рекомендується використовувати суворий компіляційний режим.