最后由 麻爪 于 2026-10-13 17:21 编辑
一、函数的使用
1.函数定义的基本语法格式:
返回值类型 函数名 (形参1,形参2 ···)
{
大括号里的实现代码
}
其中 返回值类型、函数名以及小括号里的参数列表,称作函数的头部,简称函数头;大括号和它里面的实现代码,称作函数的主体,简称函数体;函数头和函数体合在一起,叫做函数的定义。
不能在一个函数内部,再声明或者定义另一个函数(函数不能嵌套定义)。
2.函数声明
如果函数定义写在调用它的代码之后,那调用之前必须先写一句函数声明(原型),否则编译器会直接报错;如果函数的完整定义,写在所有调用它的代码之前,那就完全不需要额外写函数声明。系统也不会报错。
因为MQL5 遵循 C 体系的编译规则:代码从上往下逐行扫描,编译器遇到函数调用时,必须已经知道这个函数的「名字、参数数量 / 类型、返回值类型」,才能校验调用是否合法。如果调用之前,系统没有见过该函数,表示不认识,编译器就会直接报「未定义的函数」错误。
所谓函数声明,就是在调用之前先写一行函数头,不需要写函数体(写了函数体就相当于把函数定义写完了),先告诉编译器 “有这么个函数,参数类型是对的”,先让调用通过,后面再找完整的函数定义。需要稍微注意的是,在完整的函数定义中,函数头和函数体结束后都不需要写分号; ,因为函数体的花括号{}本身就有起止界限作用。但在函数声明时,因为只写了函数头,小括号不是语句起止符号,系统会认为这句没写完,必须加上分号,否则会报分号缺失错误。
3.函数调用
通过函数名 + 实参列表,触发函数内部的代码执行,并获取返回结果。这个动作就叫 “函数调用”;本质就是用实参给形参赋值,程序跳转到函数体执行逻辑,执行完毕后再回到调用处继续运行。
函数调用的基本格式如下:
函数名(实参1, 实参2, ..., 实参n);
函数调用基本语法规则:
(1)函数名必须与函数定义 / 声明的名称完全一致、大小写也必须一致(MQL5 是强大小写敏感语言);调用时不要写函数返回值类型,也不要写参数类型,只有函数定义 / 声明才写类型;一个函数的返回值就是函数`return`回来的结果,所以要求函数实际return后面的数值类型必须和函数定义时规定的返回值类型一致,不一致将报错。
(2)实参可以是常量、变量、表达式、另一个函数的返回值,必须是有确定值的内容;顺序必须和形参一一对应,类型必须兼容(比如形参是`double`,传`int`会自动转换,但传字符串会报错);多个实参用英文逗号分隔;有默认值的形参,对应的实参可以省略,只能从右往左依次省略,不能跳过中间的参数;如果函数没有形参,调用时圆括号也必须保留,里面留空即可,不能省略括号。
(3)调用函数后如果本语句结束,小括号后面必须要有分号做语句终止符号。
4.函数的形参和实参
(1)函数的形参,又叫形式参数,是写在函数定义的参数列表里,也就是函数头的括号中;它是函数内部的局部变量,只是一个 “占位符”,代表 “这里会接收一个数据”,但没有实际数据。
(2)函数的实参,又叫实际参数,是写在函数调用的括号(参数列表)里;它是调用函数时传入的实际数据,可以是常量、变量、表达式、甚至另一个函数调用的返回值,必须有确定的数值;代表着把一个具体的值传给对应的形参。
(3)通常情况下,把外部数据传递给函数的形参,是”传值调用“,这个过程中,函数得到的是外部数据的复制品,在本函数作用域内部,怎么折腾这个数据,都不会影响外部原来那个”源数据“;但是使用”引用调用“时,在形参前加 `&` 符号标记,就不是复制数值了,而是把外面的实参变量本身直接绑定给形参,函数内部修改处理该参数,源数据也会跟着变。所以可以把函数的参数列表按作用可以分为三类:输入参数、输出参数、输入输出参数;那些通过「引用传递(&)」来修改外部数据,实现函数结果输出的目的的参数,就是输出参数;因为函数的返回值只能有一个,如果仅把函数的返回值单做它的输出,受限太多,而使用引用传递输出函数处理结果,理论上可以让函数有无数个输出结果。
(4)虽然函数接受表达式甚至另一个有返回值的函数做参数,但是调用函数的时候,系统会先把每个参数位置的表达式全部算出具体数值,再把这些数值传给函数。也就是说,函数参数里的各种表达式的执行优先于参数传递,传给函数的一定是算好的确定数值,不会把表达式本身传进去。
(5)编译代码的时候,系统会自动检查:你传的参数数量、类型,和函数定义的形参是不是匹配,如果不匹配,系统会报错误。但是系统不会检查你传递的数据顺序是否正确,本质上它也无法判断你输入的几个参数是否和你定义函数时设计的参数一一匹配,比如第一个参数是旧价格,第二个参数应该是新价格,但你传参数时候搞反了,系统只知道这两个都是浮点数,不知道哪个新哪个旧,照样运行,但逻辑就完全错误了。
我们之前举过一个例子,一个函数好比是一台材料加工机械。现在我们具化一点,比如混凝土搅拌机,它需要输入四种原材料:石子、水泥、沙子、水,可以输出一种加工品:混凝土,所以它就有五个参数(石子,水泥,沙子,水,混凝土),前四个是它的输入参数,后一个是它的输出参数,有点特别,指的是在旁边等着的混凝土罐车——输出参数的容器。四个参数都是按千克算的,数据类型都是浮点数,输出参数是按立方米算的,也是浮点数。参数的前后顺序代表着材料投放的顺序。
现在这机器准备完毕,你也得到了配料表,知道应该一次放多少千克的材料,获得多少立方的成品,但毕竟还没有真的材料放进去,那你拿到的配料表只是”形式参数“,代表它应该输入什么数据。当别人运来若干公斤石子、水泥、沙子、和水之后,相当于给函数传来了参数,你把它们实际投入机器,这些就是”实际参数“。
常规值传递,相当于水泥搅拌站只有标准样品材料,那些不能动,运材料的人只能照规格买复制品,你就算加工了一千吨一万吨材料,原来仓库里那点样品都不受任何影响。
所谓默认参数,就像是配料表里一个固定不变的输入值,无论其他参数怎么折腾,这一项都是固定的,那就没必要每次都写明白,可以省略掉,机器固定输入默认数量的材料就好了。
但有的时候会犯错误,配料表(形参)上明明写的是多少公斤(浮点数)水泥,结果你丢进去多少包(整型数据)水泥,设备会发现输入参数类型不符,配比错误;或者配料表要求先放石子,你先放的水,都是按公斤放的,系统暂时查不出毛病,照常运转,但最后得到的混凝土依旧配比错误,不能使用。
如果你传入了正确的输入参数,那个引用传递&就好像是传送带,把正确的处理结果也就是混凝土放进旁边等待的水泥罐车里去。本函数执行完毕。但如果搅拌站仓库里有现成的原材料,你每次加工都是直接从那里取,随着设备的运行,不但产出物混凝土在变化,各种原材料(源数据)也在变化,那这些输入数据就变成了”输入输出“数据,需要通过引用传递&把函数的作用效果反馈给源数据,做出相应的修改。 |