Häufige Fehler in MQL4 Programmen und wie man sie vermeidet
Einführung
Einige ältere Programme können Fehler zurückgeben in der neuen Version des MQL4 Compiler.
Um kritisches Beenden von Programmen zu vermeiden, hat die vorherige Compiler Version viele Fehler in der Laufzeitumgebung behandelt. Zum Beispiel Division durch Null oder ein Array außerhalb des Bereichs sind kritische Fehler und führen in der Regel zum Programmabsturz. Solche Fehler treten nur in einigen uständen für bestimmte Werte von Variablen auf. Lesen Sie diesen Artikel, um zu wissen, wie solche Fälle zu behandeln sind.
The new compiler can detect actual or potential sources of errors and improve code quality.
In diesem Artikel besprechen wir mögliche Fehler, die bei der Erstellung von alten Programmen erkannt werden können und sehen, wie sie zu beheben sind.
1 Kompilierungsfehler
Wenn ein Programmcode Fehler enthält, kann er nicht kompiliert werden.
Um alle Fehler vollständig zu kontrollieren, ist es empfehlenswert den strengen Kompilierungsmodus zu verwenden, der mit der folgenden Anweisung festgelegt wird:
#property strict
Dieser Modus vereinfacht die Fehlersuche erheblich.
1.1. Identifier deckt sich mit einem reservierten Wort
Wenn der Name einer Variable oder Funktion übereinstimmt mit einem der reservierten Wörter
int char[]; // incorrect int char1[]; // correct int char() // incorrect { return(0); }
gibt der Compiler eine Fehlermeldung zurück:
Abbildung 1. Fehler "unexpected token" und "name expected"
Um diesen Fehler zu beheben, müssen Sie den richtigen Namen für die Variable oder Funktion verwenden.
1.2. Sonderzeichen in den Namen von Variablen und Funktionen
Wenn Namen von Variablen oder Funktionen Sonderzeichen enthalten ($, @, Punkt):
int $var1; // incorrect int @var2; // incorrect int var.3; // incorrect void f@() // incorrect { return; }
gibt der Compiler eine Fehlermeldung zurück:
Abbildung 2. Fehler "unknown symbol" und "semicolon expected"
Um diesen Fehler zu beheben, müssen Sie den richtigen Namen für die Funktion oder Variable verwenden.
1.3. Fehlerhafte Verwendung des Switch Operator
In der alten Version des Compilers konnten Sie alle Werte in Ausdrücken und Konstanten des Switch Operator verwenden:
void start() { double n=3.14; switch(n) { case 3.14: Print("Pi");break; case 2.7: Print("E");break; } }
In dem neuen Compiler müssen Konstanten und Ausdrücke Integers sein, wodurch Fehler auftreten, wenn Sie versuchen solche Konstruktionen zu verwenden:
Abbildung 3. Fehler "illegal switch expression type" und "constant expression is not integral"
In solchen Fällen können Sie expliziten Vergleich nummerischer Werte verwenden, zum Beispiel:
void start() { double n=3.14; if(n==3.14) Print("Pi"); else if(n==2.7) Print("E"); }
1.4. Rückgabewerte von Funktionen
Alle Funktionen, ausgenommen void, müssen den Wert des deklarierten Typs zurückgeben. Zum Beispiel:
int function()
{
}
In strengem Kompilierungsmodus tritt ein Fehler auf:
Abbildung 4. Fehler "not all control paths return a value"
Im Standard-Kompilierungsmodus gibt der Compiler eine Warnung zurück:
Abbildung 5. Warnung "not all control paths return a value"
Wenn der zurückgegebene Wert nicht mit dem deklarierten übereinstimmt:
int init() { return; }
wird während der strengen Kompilierung ein Fehler erkannt:
Abbildung 6. Fehler "function must return a value"
Im Standard-Kompilierungsmodus gibt der Compiler eine Warnung zurück:
Abbildung 7. Warnung 'return - function must return a value"
Um solche Fehler zu beheben, fügen Sie den return Operator mit dem Rückgabewert in der Code der Funktion ein.
1.5. Arrays in Funktion Argumenten
In Funktionsargumenten werden Arrays jetzt nur durch Referenz übergeben.
double ArrayAverage(double a[]) { return(0); }Im strengen Kompilierungsmodus wird dieser Code einen Fehler verursachen:
Figure 8. Compiler error "arrays passed by reference only"
Im Standard-Kompilierungsmodus gibt der Compiler eine Warnung zurück:
Abbildung 9. Compiler-Warnung "arrays passed by reference only"
Um diesen Fehler zu beheben, müssen Sie explizit angeben, dass das Array durch Referenz übergeben wird, durch hinzufügen des Präfix & vor den Namen des Array:
double ArrayAverage(double &a[]) { return(0); }
Es ist zu beachten, dass nun konstante Arrays (Time[], Open[], High[], Low[], Close[], Volume[]) nicht durch Referenz übergeben werden können. Beispielsweise der folgende Aufruf:
ArrayAverage(Open);
unabhängig von dem Kompilierungsmodus führt dies zu einem Fehler:
Abbildung 10. Fehler 'Open' - constant variable cannot be passed as reference
Um diese Fehler zu vermeiden, kopieren Sie die erforderlichen Daten von dem konstanten Array:
//--- an array that stores open price values double OpenPrices[]; //--- copy the values of open prices to the OpenPrices[] array ArrayCopy(OpenPrices,Open,0,0,WHOLE_ARRAY); //--- function call ArrayAverage(OpenPrices);
2. Laufzeitfehler
Fehler die während der Ausführung des Programmcode auftreten, werden Laufzeitfehler genannt. Solche Fehler hängen in der Regel von dem Zustand des Programms ab und sind verbunden mit inkorrekten Werten der Variablen.
Zum Beispiel, wenn eine Variable als Index für Array-Elemente verwendet wird, wird sein negativer Wert unweigerlich zu dem Array out of Range Fehler führen.
2.1. Array out of Range
Dieser Fehler tritt häufig bei Indikatoren auf, wenn sie auf den Indikator-Puffer zugreifen. Die IndicatorCounted() Funktion gibt die Anzahl der Balken unverändert seit dem letzten Indikator-Anruf zurück. Indikatorwerte auf vorher berechnete Balken müssen nicht neu berechnet werden, so müssen Sie für schnellere Berechnungen nur die letzten paar Balken verarbeiten.
Die meisten Indikatoren, die diese Berechnungsoptimierung verwenden wie sehen aus wie folgt:
//+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int start() { //--- some calculations require no less than N bars (e.g. 100) if (Bars<100) // if less bars are available on a chart (for example on MN timeframe) return(-1); // stop calculation and exit //--- the number of bars that have not changed since the last indicator call int counted_bars=IndicatorCounted(); //--- exit if an error has occurred if(counted_bars<0) return(-1); //--- position of the bar from which calculation in the loop starts int limit=Bars-counted_bars; //--- if counted_bars=0, reduce the starting position in the loop by 1, if(counted_bars==0) { limit--; // to avoid the array out of range problem when counted_bars==0 //--- we use a shift of 10 bars back in history, so add this shift during the first calculation limit-=10; } else //--- the indicator has been already calculated, counted_bars>0 { //--- for repeated calls increase limit by 1 to update the indicator values for the last bar limit++; } //--- the main calculation loop for (int i=limit; i>0; i--) { Buff1[i]=0.5*(Open[i+5]+Close[i+10]) // values of bars 5 and 10 bars deeper to history are used } }
Oft wird der Fall von counted_bars == 0 falsch behandelt (die anfängliche Grenzposition sollte durch den Wert gleich 1 + maximale Index in Bezug auf die Schleifenvariable reduziert werden).
Denken Sie auch daran, dass der wir zu dem Zeitpunkt der start() Funktion Ausführung auf Elemente von Arrays von Indikator Puffer von 0 bis Balken ()-1 zugreifen können. Wenn Sie mit den Arrays arbeiten müssen, die nicht Indikator-Puffer sind, erhöhen Sie ihre Größe mit der ArrayResize() Funktion in Übereinstimmung mit der aktuellen Größe des Indikator-Puffer. Der maximale Index des Elements zu Adresse kann auch empfangen werden, durch den Aufruf von ArraySize() mit einem der Indikator Puffer als Argument.
2.2. Division durch Null
Der Division durch Null-Fehler tritt auf, wenn ein Teiler in einer Division gleich Null ist:
void OnStart() { //--- int a=0, b=0,c; c=a/b; Print("c=",c); }
Wenn Sie dieses Skript ausführen, erscheint eine Fehlermeldung in der Experten Registerkarte und das Programm schließt sich:
Abbildung 11. Fehlermeldung "zero divide"
Normalerweise tritt dieser Fehler auf, wenn der Wert des Divisors durch die Werte von irgendwelchen externen Daten bestimmt wird. Zum Beispiel, wenn Handelsparameter analysiert werden, ist der Wert der verwendeten Marge gleich 0, wenn es keine offenen Aufträge sind. Ein weiteres Beispiel: wenn die analysierten Daten aus einer Datei gelesen werden, können wir nicht den korrekten Betrieb garantieren, wenn die Datei nicht vorhanden ist. So sollten Sie solche Fälle berücksichtigen und sie richtig verarbeiten.
Der einfachste Weg ist es, den Divisor vor der Divisionsoperation zu überprüfen und einen falschen Parameterwert berichten:
void OnStart() { //--- int a=0, b=0,c; if(b!=0) {c=a/b; Print(c);} else {Print("Error: b=0"); return; }; }
Dies verursacht keinen kritischen Fehler, aber eine Fehlermeldung über einen inkorrekten Parameterwert erscheint und das Programm fährt herunter:
Abbildung 12. Falsche Teiler Nachricht
2.3. Die Verwendung von 0 statt NULL für den aktuellen Zeichen
In der alten Version des Compilers konnte 0 (Null) als Argument in Funktionen verwendet werden, die die Spezifikation eines Finanzinstruments erfordern.
Zum Beispiel könnte der Wert des Moving Average technischen Indikator für die aktuelle Symbol wie folgt angefordert werden:
AlligatorJawsBuffer[i]=iMA(0,0,13,8,MODE_SMMA,PRICE_MEDIAN,i); // incorrect
In dem neuen Compiler müssen Sie explizit NULL bestimmen, um das aktuelle Zeichen anzugeben:
AlligatorJawsBuffer[i]=iMA(NULL,0,13,8,MODE_SMMA,PRICE_MEDIAN,i); // correct
Zusätzlich können das aktuelle Symbol und der Chart-Zeitrahmen festgelegt werden, mit den Symbol() und Period() Funktionen.
AlligatorJawsBuffer[i]=iMA(Symbol(),Period(),13,8,MODE_SMMA,PRICE_MEDIAN,i); // correct
2.4. Unicode strings und ihr Verwendung in einer DLL
Strings werden jetzt durch eine folge von Unicode-Zeichen dargestellt.
Denken Sie daran, diese und verwenden Sie entsprechende Windows-Funktionen . Wenn zum Beispiel die wininet.dll Bibliothek statt InternetOpenA() und InternetOpenUrl() verwenden, sollten Sie InternetOpenUrlA() und InternetOpenQ() aufrufen.
Die interne Struktur der Strings hat sich in MQL4 geändert (sie nimmt jetzt 12 Bytes) und die MqlString Struktur sollte verwendet werden, wenn Strings an DLL übergeben werden:
#pragma pack(push,1) struct MqlString { int size; // 32 bit integer, contains the size of the buffer allocated for the string LPWSTR buffer; // 32 bit address of the buffer that contains the string int reserved; // 32 bit integer, reserved, do not use }; #pragma pack(pop,1)
2.5. File sharing
In der neuen MQL4, FILE_SHARE_WRITE und FILE_SHARE_READ Flaggen sollten ausdrücklich für die gemeinsame Nutzung angegeben werden, wenn Dateien geöffnet werden.
Wenn die Flaggen fehlen, wird die Datei im exklusiven Modus geöffnet und kann nicht von jemand anderem geöffnet werden, bis sie von dem Benutzer geschlossen wird, der sie geöffnet hat.
Zum Beispiel, bei der Arbeit mit Offline-Charts sollten geteilte Flags explizit angegeben werden:
// 1-st change - add share flags ExtHandle=FileOpenHistory(c_symbol+i_period+".hst",FILE_BIN|FILE_WRITE|FILE_SHARE_WRITE|FILE_SHARE_READ);
Für weitere Details lesen Sie bitteOffline Charts in dem Neuen MQL4.
2.6. Datetime Konvertierung
Die Konvertierung von datetime in einen String, ist abhängig von dem Kompilierungsmodus:
datetime date=D'2014.03.05 15:46:58'; string str="mydate="+date; //--- str="mydate=1394034418" - old compiler, no directive #property strict in the new compiler //--- str="mydate=2014.03.05 15:46:58" - new compiler with the directive #property strict
Zum Beispiel, zu versuchen mit Dateien zu arbeiten, deren Name einen Doppelpunkt enthält, verursacht einen Fehler.
3. Compiler Warnungen
Compiler Warnungen sind informativ und keine Fehlermeldungen, aber sie geben mögliche Fehlerquellen an
.
Ein deutlicher Code sollte keine Warnungen enthalten.
3.1. Namen von globalen und lokalen Variablen stimmen überein
Wenn Variablen auf den globalen und lokalen Ebenen gleiche Namen haben:
int i; // a global variable void OnStart() { //--- int i=0,=0; // local variables for (i=0; i<5; i++) {j+=i;} PrintFormat("i=%d, j=%d",i,j); }
zeigt der Compiler eine Warnung mit der Zeilennummer, in der die globale Variable deklariert ist:
Abbildung 13. Warnung "declaration of '%' hides global declaration at line %"
Um solche Warnungen zu beheben, korrigieren Sie die Namen globaler Variablen.
3.2. Fehlpaarung von Typen
Der neue Compiler hat eine neue typecasting Operation.
#property strict void OnStart() { double a=7; float b=a; int c=b; string str=c; Print(c); }
In strengem Kompilierungsmodus zeigt der Compiler Warnungen, wenn Typen nicht übereinstimmen:
Abbildung 14. Warnungen "possible loss of data due to type conversion" und "implicit conversion from 'number' to 'string'
In dem Beispiel warnt der Compiler vor möglichem Verlust an Genauigkeit durch zugewiesene unterschiedliche Datentypen und impliziert Konvertierung von int zu string.
Um die Warnungen zu beheben, verwenden Sie explizites Typecasting:
#property strict void OnStart() { double a=7; float b=(float)a; int c=(int)b; string str=(string)c; Print(c); }
3.3. Ungenutzte Variablen
Das Vorhandensein von Variablen, die nicht im Programmcode verwendet werden (überflüssige Einheiten), ist keine gute Angewohnheit.
void OnStart() { int i,j=10,k,l,m,n2=1; for(i=0; i<5; i++) {j+=i;} }
Berichte über solche Variablen werden unabhängig von dem Kompilierungsmodus angezeigt:
Abbildung 15. Warnung "variable '%' not used'
Um es zu beheben, entfernen Sie alle ungenutzten Variablen aus Ohrem Code.
Fazit
Der Artikel beschreibt allgemeine Probleme, die bei der Kompilierung von Programmen die Fehler enthalten auftreten können
In allen Fällen ist es empfohlen den strengen Kompilierungsmodus für das Programm Debuggen zu nutzen.
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/1391
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.