Häufige Fehler in MQL4 Programmen und wie man sie vermeidet

MetaQuotes | 4 Mai, 2016

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
  2. Laufzeitfehler
  3. Compiler Warnungen

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:

Figure 1. Errors "unexpected token" and "name expected"

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:

Figure 2. Errors "unknown symbol" and "semicolon expected"

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:

Figure 3. Errors "illegal switch expression type" and "constant expression is not integral"

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:


Figure 4. Error "not all control paths return a value"

Abbildung 4. Fehler "not all control paths return a value"

Im Standard-Kompilierungsmodus gibt der Compiler eine Warnung zurück:

Figure 5. Warning "not all control paths return a value"

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:

Figure 6. Error "function must return a value"

Abbildung 6. Fehler "function must return a value"

Im Standard-Kompilierungsmodus gibt der Compiler eine Warnung zurück:

Figure 7. Warning 'return - function must return a value"

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"

Figure 8. Compiler error "arrays passed by reference only"

Im Standard-Kompilierungsmodus gibt der Compiler eine Warnung zurück:

Figure 9. Compiler warning "arrays passed by reference only"

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:

Figure 10. Error 'Open' - constant variable cannot be passed as reference

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:

Figure 11. Error message "zero divide"

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:

Figure 12. Incorrect divider message

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:

Figure 13. Warning "declaration of '%' hides global declaration at line %"

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:

Figure 14. Warnings "possible loss of data due to type conversion" and "implicit conversion from 'number' to 'string'

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:

Figure 15. Warning "variable '%' not used'

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.