MQL5语言帮助文件人话版(十八)数据类型之typedef

| 发表于 7 小时前 | 显示全部楼层 |复制链接
最后由 麻爪 于 2026-9-15 14:00 编辑

第十八章  typedef

一、typedef
关键字typedef 不是创造新的数据类型,它只是给已经存在的类型,起一个别名(外号)。编译器底层看待它,和原来的类型完全一模一样。
语法:typedef 原有类型 新别名;
比如:
  1. // 给 int 起别名,叫 ZB_Integer
  2. typedef int ZB_Integer;
  3. void OnTick()
  4. {
  5.     ZB_Integer a = 100; // ZB_Integer 等价于 int
  6.     Print(a);
  7. }
复制代码
如果单纯只是为了给数据类型改个名字,typedef 确实性价比很低,还容易增加阅读负担,如果没有特殊用途,新手不推荐滥用 typedef。实际上在比较简单的EA策略中,这个关键词几乎用不到。
但如果你编了一个很长长的结构体、类的名字,或者你要引用的结构、类的名字又臭又长,用 typedef来更换一下它们的名字,看起来更简洁。但如果 typedef只拿来改名字,也不建议使用,尤其对于新手,会产生混肴,改着改着就不知道谁是谁了。
typedef` 定义的类型别名,只服务于读代码的人和后期维护,说白了就是人看着方便,改着方便;但编译完成之后,对程序运行逻辑、执行效率、内存布局完全没有任何影响,编译器看待别名和原来的本名是一模一样的东西。
对于新手来说,可以全程忽略typedef 这个关键词,即使完全不用它,你写的EA也没啥影响。
但在处理函数指针方面,typedef确实也会带来一定的便利,想了解的可以钻研一下,不学这个也没啥影响。
二、typedef 和函数指针(一)函数指针
我们先看一下没有所谓“函数指针”的情况下,系统是如何调用函数的:
示例中的三个策略函数,是作者自己写的,用来演示三个指标,大括号里代表该指标函数的处理过程,为了简化演示,只是让输入价格各自乘以一个系数,最终返还三个挂单价。
  1. //  自编的三个示例策略函数
  2. double ZB_SignalMA(double price) { return price * 1.01; }
  3. double ZB_SignalRSI(double price) { return price * 0.99; }
  4. double ZB_SignalMACD(double price) { return price * 1.005; }
复制代码
再设计一个EA输入参数(input),相当于人工选择策略1,也可以人工改成2或者3。这也是简化写法实际上选哪个策略函数,也是可以写成策略,让系统自己分析出来。
  1. input int StrategyType = 1; // 1代表MA策略,2代表RSI策略,3代表MACD策略
复制代码

下面是没有函数指针,硬写的策略选择代码:
  1. daibvoid OnTick()
  2. {
  3.     double ask = Ask;
  4.     double signal;
  5.    
  6.     if(StrategyType == 1)  //如果选择策略1信号
  7.     {
  8.         signal = ZB_SignalMA(ask);//拿到策略1信号的价格
  9.     }
  10.     else if(StrategyType == 2) //如果选择策略2信号
  11.     {
  12.         signal = ZB_SignalRSI(ask);//拿到策略2信号的价格
  13.     }
  14.     else if(StrategyType == 3)//如果选择策略3信号
  15.     {
  16.         signal = ZB_SignalMACD(ask);//拿到策略3信号的价格
  17.     }
  18.    
  19.     ······
  20. }
复制代码

如果你的EA策略只有一个,可以忽略函数指针的写法,如果你的EA策略函数有多个,那在这里需要你把所有可选择的函数都写出来,要写一大堆if/else,如果你需要在多个函数之中选一个来运行,可以用函数指针来处理,代码看上去会比较清爽,下面是常规函数指针的写法:
  1. // 三个策略函数(不变)
  2. double ZB_SignalMA(double price) { return price * 1.01; }
  3. double ZB_SignalRSI(double price) { return price * 0.99; }
  4. double ZB_SignalMACD(double price) { return price * 1.005; }
  5. //策略选择也不变
  6. input int StrategyType = 1;
  7. // 自定义一个pSignalFunc 函数指针:接收1个double参数,返回double,
  8. double (*pSignalFunc)(double);//函数指针的语法:数据类型 (*自定义函数指针名)(数据类型),指得是函数返回值的类型,而不是函数指针的类型
  9. // 在初始化的时候,一次性绑定好函数(只执行一次),相当于让函数指针记住一个指向
  10. int OnInit()
  11. {
  12.     if(StrategyType == 1)
  13.         pSignalFunc = ZB_SignalMA;
  14.     else if(StrategyType == 2)
  15.         pSignalFunc = ZB_SignalRSI;
  16.     else if(StrategyType == 3)
  17.         pSignalFunc = ZB_SignalMACD;
  18.     return(INIT_SUCCEEDED);
  19. }
  20. void OnTick()
  21. {
  22.     double signal = pSignalFunc(Ask); //调用方式完全没变!
  23.     Print("signal = ",signal); 
  24.     // 后续下单逻辑
  25. }
复制代码

需要说明的是,这段代码只是演示函数指针如何使用,在实际EA中,完全像举例中这么写纯属多此一举。你会发现上面的代码中,一大堆的if/else还是存在,只是换了个地方罢了。也就是说如果你只需要在某处调用一次某个函数,哪怕这些函数是多选一的,还是老老实实写if/else比较靠谱。但如果你的函数不只是一个位置调用,而是多处调用,用函数指针来写,才能实现所谓的清爽和容易维护的效果。因为一处调用就代表着要写一处if/else,有多少处调用就要写多少个if/else,而改用函数指针来写时,就只需要写一处if/else了。当然也可以封装一个if/else函数,在需要调用的地方调用就好了,也不是非得写函数指针。
(二)typedef 和函数指针
typedef可以给【同一类的函数原型】起别名。要理解这个用法,我们需要先介绍一些函数有关的概念。
程序中的函数,至少包含三部分:
一部分是函数原型,包括函数的参数和返回值,可以近似理解为输入和输出数据;其中函数的参数部分又叫做函数的签名,所谓函数签名,就是用来唯一描述一个函数 “长什么样、怎么调用” 的一套信息:包括函数的参数数量、每个参数的类型,以及参数的排列顺序。一些复杂函数,不止一个参数,各参数的类型也不尽相同。
第二部分是函数定义部分,也就是大括号{ }中包含的那些函数代码,是实现函数逻辑的主体。
第三部分是函数名,就是给函数起的名字,可以自定义,也有系统自带的。
MQL5/C 语言体系里,一个函数的类型,就是由它完整的函数原型决定的。函数原型 = 返回值类型 + 函数签名(参数数量、类型、顺序)。只要原型确定,这个函数的类型就确定了;函数名字不属于函数类型。
比如前面我们举的例子:double ZB_SignalMA(double price),中间的“ZB_SignalMA”是自己取的函数名字,可以换成任意函数名字,括号里的“price”也是自己取的变量名字,指代一种数据,该举例函数完全可以写成:double XXX(double XXX),如果该函数有多个输入参数,就可以写成:double XXX(double XXX, double XXX, int xxx)。
通过这个简化写法,我们会发现,一个函数最重要的两部分在简化中保留了下来,一个是返回值的类型,也就是括号外面那个“double”,一个是输入参数的类型、数量、顺序,也就是括号里面那些内容,如果多个函数的这些内容相同,我们就可以判断这些函数是同类函数。
很明显,我们的三个策略函数,就属于同类函数:double XXX(double XXX),即输入一个浮点数参数,返还一个浮点数结果的函数。我们用typedef给这个函数原型double XXX(double XXX)起个别名:
typedef double ZB_SignalFunc(double price);
“ZB_SignalFunc”就是我们起的名字,相当于我们给这一类函数起了一个类别名字,看到这个名字,就知道是这一类函数。
这时候,之前常规星号函数指针的写法:double (*pSignalFunc)(double);就可以替换成ZB_SignalFunc *pSignalFunc; ,相当不不用再写那个带括号的函数原型的格式了。
细心的朋友应该可以发现,如果我们只有一次调用,且函数只有一个参数,用typedef来写函数指针纯粹自寻烦恼,没什么实际意义,还增加了代码复杂成都。
本质上,在这种情况下写函数指针都是自寻烦恼。
只有需要多处调用,且函数参数数量、类型较多、较复杂时,用typedef来写函数指针,才能发挥它的简化作用。因为一个函数只能有一个返回值类型,但可以拥有多个输入参数,且输入参数的类型还可以各不相同,对一些复杂的函数,小括号里面写五六个参数都属正常,常规函数指针写法,在这种情况下会很长,且容易写错。
最近访问 头像模式
举报

评论 使用道具

发新帖
EA交易
您需要登录后才可以评论 登录 | 立即注册

麻爪

麻爪 D

如果你想让一个人下地狱,介绍他做交易。
如果你想让一个人在地狱里永不超生,介绍他做EA交易。

Fast跟单EA