MQL5语言帮助文件人话版(二十)数据类型之指针

| 发表于 7 小时前 | 显示全部楼层 |复制链接
第二十章 指针

原手册中这一章的题目是“目标指针”,编程界常规黑话叫“对象描述符”。因为它主要用来来存放类 / 结构体实例的描述符,用来访问堆上`new`创建出来的对象。而这一章主要讲的就是“new”操作符,该操作符操作的对象一般就是类或者结构体,它的返回值是一个指针变量,所以这一章直译为“目标指针”。
所谓返回值,就是可以归结为“结果”的数据,只要是运算符、表达式执行之后,能产出一个结果,这个结果就可以叫“返回结果 / 返回值”,只是通常函数用得最多,但返回值并不是函数所独有的。
MQL5语言中有三种“指针”,分别是:
1.函数指针:前面聊过了,保存函数入口标识,而不是内存地址,用来调用函数、做事件回调。
2.基础类型指针:保存普通变量,比如`int*`、`double*`、`string*`的地址,使用取地址运算符`&`取地址符。用来间接读写基础变量。
3.对象描述符:主要用来来存放类 / 结构体实例的描述符,用来访问堆上`new`创建出来的对象。
至于手册中举的相关案例代码,我建议新手先不要去啃,里面同样掺杂了许多还没触及到的知识,硬啃的话,很容易劝退。所以这一章我的目的把下面几个概念讲清楚,这些新概念就相当于将来我们会遇到的生词,如果不先学明白,后面的东西也把握不住。
一.作用域
作用域,顾名思义,就是数据起作用的区域,这个区域实际上就是指从哪到哪的一段代码。我们在写EA的时候,经常会写一些所谓的“全局变量”,这种变量的作用域就是所有的代码。我们在写函数的时候,在函数的大括号里也会写一些所谓的“局部变量”,函数的大括号范围内,就是这个局部变量的作用域。
当然在MQL5语法中,并不止函数拥有大括号,比如我们前面讲过的类和结构体,也有大括号,见到大括号就可以判断为作用域的起止边界,它里面的变量只能在里面用。其他符号比如小括号、引号、尖括号、方括号之类的,就没有作用域边界的含义了。
从底层硬件角度来看,也就是从内存卡的物理层面上观察,程序一旦运行起来,整个程序都放在内存中。但变量只在程序运行到其作用域的时候才存入内存,程序走出其作用域的时候,该变量销毁,编程黑话叫“返还内存”。所以我们说,一个函数的局部变量,不能被另一个函数使用,多个函数公用的变量必须写成全局变量,就是因为那个大括号挡住了其他函数使用它内部的数据,程序运行到其他函数时,它的作用域已经走完了,它内部的变量数据已经不存在了,别的函数想用也没的用。
注意,这里说的是函数内部的变量,也就是它的输入参数,而不是输出结果。函数的代码就是函数返回值的作用域。其他函数调用本函数,就是运行这段代码,它的作用域才开始起效果。有返回值的函数,前面总会有个数据类型标识符,就是在被调用时开辟一块内存,存入本函数的返回值,函数调用完了,同样也会销毁。而其他函数调用这个函数的返回值时,必须先在自己内部定义一个新的变量,让这个变量等于被调用函数的返回值,相当于把被调用函数的返回值复制了一份放进自己的作用域中。
二、数据的内存地址
我们用普通数据类型定义变量,相当于给某一块内存位置起个名字,然后给这块内存写入或读出一个数据。数据类型大小不同,是三位数还是八位数,还是十八位数所开辟的内存大小也不同,用比特、字节这样的单位来表示。
这里有个比较绕新手脑子的地方,不是这个变量需要多大内存,数据类型就给它多少内存,而是这一种数据类型就规定了这么多内存,哪怕你用十八位数的数据类型去存储一个个位数,它所占用的内存同样是十八位数的内存。这个先后关系的理解对新手提高编程认知很重要,前面说的所谓“静态数组“、”动态数组“就是从这个角度说事儿的,静态数组一旦创立,内存大小就限定了,能存100个数,绝对放了101个,动态数组的内存与之相反,可以随着放入数据的多少时时变化。
每个新开辟的内存位置,都有一个地址编号,这个地址编号跟硬盘的硬件有关,是唯一的。但多数情况下,这个地址跟我们定义变量、赋值、读取等操作没什么关系,我们并不关心这一点。
三、基础类型指针
基础类型指针在写EA中用处不多,我们稍作了解。基础类型指针的核心在于”引用“而非”复制“。我先举个例子:
  1. void OnStart()  
  2. {   
  3. int a =10;   
  4. a = a + 1;   
  5. Print(a);  
  6. }
复制代码
OnStart()是开始事件函数,这段代码很简单,仅用作演示,含义就是系统每次开始运行时,给一个变量a加1,然后得到结果,这段代码的结果是11。很明显,这个a = a + 1是一个对初始数据的处理过程。这个例子的实际意义可以理解为计数器,a代表某个东西的数量,一直加1加1,但无论具体数字如何变,数量a这个概念是不会变的。
实际上我们会遇到很多复杂的数据处理过程,而且处理得到的结果可能不只一个,还可能会多出调用这个处理过程,实在不合适在每一处都重写一遍这个处理过程。
这时候我们倾向于把这个处理过程单独封装成一个函数,然后在OnStart()里面调用这个函数。按想当然的思路,应该是这样的:
  1. void AddOne(int num) //自定义一个函数,名为AddOne,意思是加一,输入参数是一个int数字
  2. {
  3.    num = num +1;     //该函数的处理过程,就是在输入参数上加1
  4. }
  5. void OnStart        //正常启动
  6. {
  7.    int a=10;       //设计一个变量a,给它赋值10
  8.    AddOne(a);      //调用AddOne函数,给变量加1
  9.    Print(a);       //最终得到的变量a,还是10
  10. }
复制代码
可见我们虽然调用了函数,但并没有实现给变量a加1的效果。在AddOne(a);这里,我们调用了函数void AddOne(int num),这种属于”传值调用“,调用就是让void AddOne(int num)出来工作,它的作用域开始起效,传值的意思是相当于在这个函数里面加了一行:num=a,把a的值赋给变量num,这时候内存中开辟了新的内存区域名叫num,并让它等于a,然后在给它加1,num变成了11。然后呢?这条代码运行完,函数void AddOne(int num)的作用域结束,num变量随着消失,就像啥都没发生过。
下一条 Print(a);,还是输出的void OnStart函数作用域里面那个int a=10; 的a,所以最终结果还是10。调用函数调用了个寂寞。
如果我们把void AddOne(int num)的参数换成指针变量,就不一样了:
  1. void AddOne(int *num)
  2. {
  3.    *num = *num + 1; // *num 代表:找到这个指针指向的原始变量,修改原值
  4. }
  5. void OnStart()
  6. {
  7.    int a = 10;
  8.    AddOne(&a); // &a:取a的地址(指针)传进去
  9.    Print(a); // 输出11,原始a被修改
  10. }
复制代码
这么一改,与之前的关键区别就是,num在这里不接收变量a的数字,它接收的是变量a的地址。
&是地址获取符,&a就是获得变量a的地址,也就是前面说的那个磁盘地址编号,这是一串老长老长的数字,把它当作void AddOne(int num)的参数,赋给变量num。
*num = *num + 1里的星号是解引用运算符,意思是,函数要的不是num代表的那串地址数字,而是那串地址数字所指向的变量。
void AddOne(int *num)里的int * 代表指针数据类型,num才是它的参数变量。它的参数是一个指针数据,就代表着它并不是处理自己的参数,而是处理它的参数所指向的数据。
这就是基础类型指针,最典型的用途了。当然这个例子太简单,如果真要做计数器,完全不需要用指针,按最简单的写法就好,这个例子只用于展示指针的应用情况。
有点绕,需要多想几遍。
四、指针的格式
在内存卡上,每一块内存位置都有物理编号,这个编号是一个我们可以用取地址符 & 获取。这个编号比较长,32位系统的电脑就是32个比特,64位系统的电脑就是64个比特。而指针就是专门存放这个内存地址编号的变量;通过这个地址,去间接操作另一块内存位置。比如:
  1. int a;                   //定义一个整形变量a
  2. int *p = &a;             //定义一个指针变量p,指针符号*是特征,
  3. *p=100;                 //这就是通过a的指针去操作a,给它赋值100。
复制代码
int *p = &a; 这一句,如果要断句的话,要从星号后面断,int *是一部分,代表这是一个指向int类型数据的指针型数据,int代表着指针指向的变量的类型,而不是指针自身的类型,你完全可以而且有必要把指针看作是一种不同于之前学的什么整数、浮点数、数组之类的数据类型之外的另一种数据类型;p是第二部分,它是一个指针变量,就像我们说整型变量、浮点数变量的意义一样,指针变量里面存的是地址,就是前面说的内存物理编号,这是一串64位0和1组成的二进制数字,通过取地址符 & 获取, &后面就是这个指针变量所指向的变量。
在int *p = &a;中,星号是类型描述符号,意思是描述变量p是个指针型数据。但在*p=100;中,星号*是解引用运算符,意思是要顺着指针变量p找到它所指向的变量。

这一张我基本上把指针的相关概念讲清楚了,有了这些概念,才能继续理解所谓“new”操作符、栈和堆这三个概念。
最近访问 头像模式
举报

评论 使用道具

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

麻爪

麻爪 D

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

Fast跟单EA