预处理指令就是编译前给编译器下达的“指令”,用来告诉它要包含什么文件、定义哪些宏变量、设置编译条件等等。它们都以 #
开头,并且不是普通的 MQL4 语句,是编译器在正式翻译代码前先处理的命令。
你可以把它们理解为:就像煮饭前的准备动作(洗米、放水、调程序),不是“真正做饭的步骤”,但却直接影响你这锅饭的好坏和怎么煮。
常见的预处理指令:
预处理指令 | 作用解释 |
---|---|
#property |
给EA设置一些属性,比如图标、版本、颜色等说明 |
#include |
把别的文件“包含”进来,相当于“复制粘贴”进当前文件 |
#define |
定义一个常量,像设一个全局开关或替换名词 |
#ifdef / #endif |
条件编译:当某个宏存在时才编译某段代码 |
#import |
用于调用外部 DLL 或者其它 MQ4 文件中的函数 |
预处理指令不是普通代码,是给编译器的“提前说明书”,目的是让你的代码更灵活、更易维护、更清晰,你看到 #
开头的代码,基本就是预处理指令。
预处理指令详解:
① #property
预处理器指令通常在EA源文件的最开始部分,这些指令均以井号 #
开头。MetaTrader生成的默认 EA 模板通常会包含两条预处理器指令:
#property copyright "作者名"
:用于声明版权信息,这里的 “作者名” 是您在向导中输入的作者名称。#property link "链接地址"
:用于提供相关链接,”链接地址” 是您在向导中输入的链接 (Link)。
还有一些其他的 #property
指令,但它们绝大多数是用于指标 (indicators) 或脚本 (scripts) 的特定属性设置。对于开发EA而言,通常只需要保留或设置 #property copyright
来标识该程序是您的作品。
告诉编译器:这个程序是个指标,要显示在图表主窗口。
② #include
第二种您会经常使用的预处理器指令是 #include
。这段代码意思是:请在这里插入 stdlib.mqh
文件中的内容,就像把整个文件粘贴进来一样。
正如之前提到的,包含文件(通常是 .mqh
文件)包含了函数定义和源代码,通过 #include
指令,这些代码会在您的主项目(如 EA 文件)被编译时,被完整地包含 (include) 进来。#include
指令的基本语法是:
// 包含系统提供的标准库文件 (通常用尖括号)
#include <stdlib.mqh>
// 包含用户自定义的库文件 (通常用双引号,表示相对路径)
// #include "MyFunctions.mqh"
例如,之前示例中提到的stdlib.mqh
文件是 MetaTrader 安装时自带的一个标准包含文件,其中包含了一些对编程有帮助的通用辅助函数。像所有标准包含文件一样,它通常位于 MetaTrader 安装目录下的 \MQL4\Includes
文件夹中。
③ #define
现在你写 MAX_TRADES
,编译器会自动替换为 5,类似设置一个“全局配置”。
#define
指令用于在程序中定义常量或宏。通过定义常量,您可以用一个易于理解和修改的标识符来代替程序中可能多次出现的特定数值或字符串。例如,与其在代码中反复手写一个长字符串,不如这样定义一个常量。
// 定义一个名为 MYCONSTANT 的字符串常量
#define MYCONSTANT "这是一个常量字符串"
定义之后,在您的代码中,任何出现 MYCONSTANT
的地方,在预处理阶段都会被直接替换为 "这是一个常量字符串"
。编程的一个良好实践是,使用 #define
定义的常量标识符采用全大写字母格式(如 MYCONSTANT
),以便与变量名区分开来。虽然这不是 MQL4语法的强制要求,但遵循此约定能提高代码的可读性和一致性。
④ #ifdef
#define DEBUG_MODE
#ifdef DEBUG_MODE
Print("调试模式:打印一些日志");
#endif
在这段代码中:
-
#define DEBUG_MODE
定义了一个名为DEBUG_MODE
的宏常量。这里没有给DEBUG_MODE
赋值,默认它就是一个存在的宏。 -
#ifdef DEBUG_MODE
会检查DEBUG_MODE
是否被定义,如果已经定义,就会执行Print("调试模式:打印一些日志");
语句。 -
#endif
用于结束#ifdef
块。
所以,当编译器遇到 #ifdef DEBUG_MODE
时,会检查是否定义了 DEBUG_MODE
,如果定义了,它会执行 Print()
打印调试日志。如果没有定义 DEBUG_MODE
,则 Print()
语句会被跳过。
如果还没明白,预处理指令可以类比下面这些生活中的场景:
生活场景 | 对应预处理指令作用 |
---|---|
点菜单 | #include :点哪些菜(加入哪些模块) |
标注辣度/口味 | #property :说明这道菜是什么属性(主食?辣?甜?) |
点几份 | #define :数量设定(比如定义最多5道菜) |
根据天气决定吃什么 | #ifdef :如果下雨吃火锅,否则吃烧烤 |
⑤ #import
有时您可能需要调用一个已经存在于外部已编译文件中的函数。这个外部文件可能是另一个 EA(较少见)、一个 MQL 库文件(.ex4
文件)或一个 Windows 动态链接库(.dll
文件)。在这种情况下,您可以使用 #import
指令将这些外部函数导入 (import) 到您的项目中,从而可以在您的代码中直接调用它们。
MQL 库文件 (.ex4
) 与包含文件 (.mqh
) 的主要区别在于:#include
是在编译时将源代码文本合并进来;而 #import
是在编译时建立一个链接,指向一个已编译的外部文件,然后在程序运行时去调用该外部文件中的函数。关于库文件的创建和使用,我们将在后续部分深入探讨。
#import
指令通常建议集中放在包含文件 (.mqh
) 中进行管理,特别是当需要导入多个函数时。但如果仅需导入少量函数,且没有合适的现有包含文件来放置它们,也可以直接在您的主项目代码(如 EA 文件)中使用 #import
。
关于 #import
指令的详细语法和应用示例,请参考 MQL 官方参考文档中关于“函数导入”的章节,同时也可以研究 MetaTrader 自带的 Include
文件夹下的标准库文件是如何使用 #import
的。#import
的基本语法结构如下:
// 指定要从哪个库文件导入函数 (文件名需用双引号)
#import "MyExternalLibrary.ex4"
// 声明需要导入的函数原型:返回类型 函数名(参数类型列表);
// 注意:函数签名必须与库文件中导出的函数完全一致!
double CalculateSomething(int input1, double input2);
string GetStatusMessage();
// ... 可以声明多个需要导入的函数 ...
// 使用一个单独的 #import 来结束导入声明块
#import
在上述示例结构中,我们指定了要从 MyExternalLibrary.ex4
这个库文件中导入函数。接着,我们声明了两个需要导入的函数:CalculateSomething
(它接受一个 int
和一个 double
参数,并返回 double
)和 GetStatusMessage
(它不接受参数,并返回 string
)。关键在于,这里声明的函数名、返回类型以及参数的类型和顺序必须与 MyExternalLibrary.ex4
文件实际导出的函数完全匹配。每个函数声明都以分号结束。整个导入声明以 #import "库文件名"
开始,并以一个单独的 #import
指令结束。