介紹一些較舊的程序可能在新版本的MQL4編譯器中返回錯誤。 為了避免關(guān)鍵的程序完成,以前版本的編譯器在運行環(huán)境中處理了許多錯誤。例如,除數(shù)為零或數(shù)組越界都是嚴重錯誤,并通常會導致應用程序崩潰。這些錯誤只在一些狀態(tài)下針對某些變量值而發(fā)生。閱讀這篇文章了解如何處理這樣的情況。 新的編譯器可以檢測實際或潛在的錯誤源并提高代碼質(zhì)量。 在這篇文章中,我們討論了舊程序編譯過程中檢測到的可能出現(xiàn)的錯誤,以及解決這些問題的方法。 1編譯錯誤如果程序代碼中包含錯誤,則它不能被編譯。 要完全控制所有的錯誤,建議使用嚴謹?shù)木幾g模式,它通過以下指令來設(shè)置: #property strict 這種模式大大簡化了故障排除。 1.1. 與關(guān)鍵字一致的標識 如果變量或函數(shù)的名稱與其中一個關(guān)鍵字一致 int char[]; // incorrect int char1[]; // correct int char() // incorrect { return(0); } 編譯器會返回一個錯誤信息: 圖1. 錯誤“unexpected token(非預期標記)”和“name expected(預期名稱)” 要解決這個錯誤,您需要使用變量或函數(shù)的正確名稱。 1.2. 變量和函數(shù)名的特殊字符 如果變量或函數(shù)名稱中包含特殊字符($,@,點): int $var1; // incorrect int @var2; // incorrect int var.3; // incorrect void f@() // incorrect { return; } 編譯器會返回一個錯誤信息: 圖2. 錯誤“unknown symbol(未知交易品種)”與“semicolon expected(預期分號)” 要解決這個錯誤,您需要使用正確的函數(shù)或變量名。 1.3. 使用switch操作符的錯誤 在舊版本的編譯器中,您可以在switch操作符的表達式和常量中使用任何值: void start() { double n=3.14; switch(n) { case 3.14: Print("Pi");break; case 2.7: Print("E");break; } } 在新的編譯器中,switch操作符的常量和表達式必須是整數(shù),所以當您嘗試使用這樣的結(jié)構(gòu)時會發(fā)生錯誤: 圖3. 錯誤“illegal switch expression type(非法switch表達式類型)”和“constant expression is not integral(常量表??達式不是整數(shù))” 在這種情況下,您可以使用明確的數(shù)值比較,例如: void start() { double n=3.14; if(n==3.14) Print("Pi"); else if(n==2.7) Print("E"); } 1.4. 函數(shù)返回值 除了空值外的所有函數(shù)都應該返回聲明的類型值。例如: int function()
{
} 在嚴謹?shù)木幾g模式下發(fā)生錯誤: 圖4. 錯誤“not all control paths return a value(并非所有的控制路徑返回一個值)” 在默認的編譯模式下,編譯器會返回一個警告: 圖5. 警告:“not all control paths return a value(并非所有的控制路徑返回一個值)” 如果函數(shù)的返回值與聲明的不匹配: int init() { return; } 在嚴格的編譯中會檢測錯誤: 圖6. 錯誤“function must return a value(函數(shù)必須返回一個值)” 在默認的編譯模式下,編譯器會返回一個警告: 圖7. 警告 'return - function must return a value(回報 - 函數(shù)必須返回一個值)” 要解決這樣的錯誤,添加帶有相應類型返回值的return操作符到函數(shù)代碼。 1.5. 函數(shù)參數(shù)數(shù)組 在函數(shù)參數(shù),數(shù)組現(xiàn)在只引用傳遞。 double ArrayAverage(double a[]) { return(0); }在嚴謹?shù)木幾g模式下,該代碼將導致錯誤: 圖8. 編譯器錯誤“arrays passed by reference only(數(shù)組只引用傳遞)” 在默認的編譯模式下,編譯器會返回一個警告: 圖9. 編譯器警告“arrays passed by reference only(數(shù)組只引用傳遞)” 要修復此錯誤,您必須通過在數(shù)組名稱之前添加前綴來指定數(shù)組是通過引用傳遞的: double ArrayAverage(double &a[]) { return(0); } 但應注意的是,現(xiàn)在常量數(shù)組 (Time[], Open[], High[], Low[], Close[], Volume[]) 不能引用傳遞。例如,下面的調(diào)用: ArrayAverage(Open); 無論何種編譯模式都會導致錯誤: 圖10. 錯誤'Open' - constant variable cannot be passed as reference(‘打開’ - 常量變量不能引用傳遞) 為了避免這些錯誤,從常量數(shù)組??復制所需的數(shù)據(jù): //--- 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. 運行時間錯誤程序代碼執(zhí)行過程中出現(xiàn)的錯誤稱為運行時間錯誤。這種錯誤通常是依賴于程序的狀態(tài),并與變量的不正確的值相關(guān)聯(lián)。 例如,如果變量用作數(shù)組元素的索引,其負值將不可避免地導致數(shù)組超出范圍的錯誤。 2.1. 數(shù)組超出范圍 訪問指標緩沖區(qū)時常常在指標中發(fā)生這個錯誤。該IndicatorCounted()函數(shù)返回自上次指標調(diào)用的不變的柱數(shù)。先前計算的柱的指標值不需要重新計算,所以為了更快的計算,您只需要處理最后的幾個柱。 大部分使用這種計算優(yōu)化的方法的指標看起來如下: //+------------------------------------------------------------------+ //| 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 } } 通常counted_bars==0的情況處理不當(初始限制持倉應該通過等于相對循環(huán)變量的1 +最大指數(shù)的值來降低)。 另外,請記住,在執(zhí)行start()函數(shù)的時候,我們可以從0到Bars ()-1的訪問指標緩沖區(qū)的數(shù)組元素。如果您需要使用無指標緩沖區(qū)的數(shù)組,那么按照指標緩沖區(qū)的當前大小使用ArrayResize()函數(shù)來增加其大小。也可以通過調(diào)用用作參數(shù)的指標緩沖區(qū)的 ArraySize()來獲得元素地址的最大指數(shù)。 2.2. 除數(shù)為零 當除法運算中除數(shù)為零時則會發(fā)生零除的錯誤: void OnStart() { //--- int a=0, b=0,c; c=a/b; Print("c=",c); } 當您運行這個腳本時,專家選項卡會出現(xiàn)一條錯誤的消息,并且程序關(guān)閉: 圖11. 錯誤消息“zero divide(除數(shù)為零)” 當除數(shù)的值由任何外部數(shù)據(jù)值來決定時,通常會出現(xiàn)此錯誤。例如,如果交易參數(shù)進行分析,如果沒有新建訂單,那么已用預付款的值等于0。另一個例子:如果要從一個文件讀取分析數(shù)據(jù),如果該文件不可用,那么我們也不能保證正確的操作。所以您應該考慮到這樣的情況并正確地處理它們。 最簡單的方法是除法運算前檢查除數(shù)并報告不正確的參數(shù)值: void OnStart() { //--- int a=0, b=0,c; if(b!=0) {c=a/b; Print(c);} else {Print("Error: b=0"); return; }; } 這不會導致嚴重的錯誤,但是不正確參數(shù)值的消息一出現(xiàn)則程序即關(guān)閉: 圖12. 不正確的除數(shù)消息 2.3. 當前字符用0替代NULL 在舊版本的編譯器中0(零)可用作滿足金融工具規(guī)范的函數(shù)參數(shù)。 例如,當前交易品種的移動平均線技術(shù)指標的值可能被要求如下: AlligatorJawsBuffer[i]=iMA(0,0,13,8,MODE_SMMA,PRICE_MEDIAN,i); // incorrect 在新的編譯器中您應該明確地指定NULL來規(guī)定當前的交易品種: AlligatorJawsBuffer[i]=iMA(NULL,0,13,8,MODE_SMMA,PRICE_MEDIAN,i); // correct 此外,當前交易品種和圖表周期可使用Symbol()和Period()函數(shù)來指定。 AlligatorJawsBuffer[i]=iMA(Symbol(),Period(),13,8,MODE_SMMA,PRICE_MEDIAN,i); // correct 2.4. Unicode字符串和它們在DLL中的使用 字符串現(xiàn)在表示為Unicode字符序列。 記住這一點,并使用適當?shù)腤indows函數(shù)。例如,使用wininet.dll庫來替代 InternetOpenA() 和InternetOpenUrlA(),您應該調(diào)用InternetOpenW() 和InternetOpenUrlW()。 該字符串的內(nèi)部結(jié)構(gòu)在MQL4中(現(xiàn)在只需要12個字節(jié))發(fā)生了變化,當傳遞字符串到DLL時應使用MqlString結(jié)構(gòu): #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. 文件共享 在新MQL4中,F(xiàn)ILE_SHARE_WRITE和FILE_SHARE_READ標志應明確地指定以便打開文件時共享使用。 如果標志不存在,那么該文件以單獨模式打開,直到文件由打開它的用戶關(guān)閉才可以被其他人打開。 例如,使用離線圖表時共享標志應明確指定: // 1-st change - add share flags ExtHandle=FileOpenHistory(c_symbol+i_period+".hst",FILE_BIN|FILE_WRITE|FILE_SHARE_WRITE|FILE_SHARE_READ); 欲了解更多詳情,請閱讀新MQL4的離線圖表。 2.6. 日期時間轉(zhuǎn)換 轉(zhuǎn)換日期時間為一個字符串現(xiàn)在取決于編譯模式: 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 例如,嘗試使用文件名中包含冒號的文件會導致錯誤。 3. 編譯器警告編譯器警告是信息性而非錯誤的訊息,但它們指出了可能的錯誤來源。 一個清晰的代碼不應該包含警告。 3.1. 全局和局部變量名稱一致 如果全局和局部各級變量具有相似的名稱: int i; // a global variable void OnStart() { //--- int i=0,j=0; // local variables for (i=0; i<5; i++) {j+=i;} PrintFormat("i=%d, j=%d",i,j); } 編譯器會顯示指出全局變量的聲明行號的警告: 圖13. 警告“declaration of '%' hides global declaration at line %(聲明'%'隱藏在行%的全局聲明)” 要解決這樣的警告需要修正全局變量的名稱。 3.2. 類型不匹配 新的編譯器有一個新的類型轉(zhuǎn)換操作。 #property strict void OnStart() { double a=7; float b=a; int c=b; string str=c; Print(c); } 在嚴謹?shù)木幾g模式下如果類型不匹配則編譯器會顯示警告: 圖14. 警告“possible loss of data due to type conversion(由于類型轉(zhuǎn)換可能丟失數(shù)據(jù))”和“implicit conversion from 'number' to 'string'(從'數(shù)字'到'字符串'的隱式轉(zhuǎn)換)” 在這個示例中,編譯器警告關(guān)于分配的不同數(shù)據(jù)類型的可能的精確度損失和從整數(shù)到字符串的隱式轉(zhuǎn)換。 要解決此警告需要使用明確的類型轉(zhuǎn)換: #property strict void OnStart() { double a=7; float b=(float)a; int c=(int)b; string str=(string)c; Print(c); } 3.3. 未使用的變量 程序代碼中存在不使用的變量(多余的實體)不是一個好習慣。 void OnStart() { int i,j=10,k,l,m,n2=1; for(i=0; i<5; i++) {j+=i;} } 無論何種編譯模式都會顯示這些變量的報告: 圖15. 警告“variable '%' not used('%'變量未使用)” 要修復它,需要從代碼中移除未使用的變量。 結(jié)論本文描述了包含錯誤的舊程序的編譯過程中可能出現(xiàn)的常見問題。 在所有情況下,建議使用嚴謹?shù)木幾g模式來調(diào)試程序。 |
|