本帖最后由 嫂子 于 2025-3-26 13:27 编辑
首先:易语言没有指针 只有地址 有人说不就是内存地址么下面就说说区别和联系
先说地址
就是内存中的位置 也就是内存地址 指示的是内存中的位置信息
再说指针
指针是一个类型 本质是 带类型信息的地址
比如 整数指针 就是指向整数(4字节)的一个地址。
最后说 为什么要有指针。
指针类型 是一个类型
所有 类型检查 等等安全性方面的他都有。
而地址只是个整数位置 所以安全性比好做检测,
比如你是否能保证你的参数传进来的是个地址呢?
比如整数 就是一个4这字节内存而已 为什么需要整数类型呢?
最佳实践:
指针类型 是带类型信息(内存布局)的地址,所以你就可以直接吧指针(已经指向了实际数据)直接反向读取回类型。
案例理解知识储备:
后续我用Pascal简易代码说明所以需要一点点知识预备:
Delphi(Pascal)中
0.Pascal 赋值用:= (C系语言用=赋值) 比较用= (C系语言用== 比较)
1.@符号 作用是取地址
2.^+类型 是指向类型的一个地址 也就是一个类型指针 比如pInteger=^Integer;pInteger其实就只是个别用而已。这一点可以从Delphi的原代码中得到验证。
3.指针变量+^ 是取回该指针指向的 实际变量数据。
比如 pInteger类型是指向整数的指针类型 那么
假设
var A:pInteger; //A是指向Integer整数的指针
var B:=A^; //B 就是A这个指针指向的实际变量 也就是一个Integer整数
好了有这点知识储备就足够看懂Delphi的代码了。
实际案例1:异步数据传递 (比如给线程传递数据)
比如有一个结构 学生信息 包含姓名和年龄2个字段[Pascal] 纯文本查看 复制代码 type
TStudent=Record //结构体
Name:string; //姓名 字符串类型
Age:Integer; //年龄 整数类型
end;
pStudent=^TStudent;//申明一个pStudent类型
众所周知 结构体是值类型 也就是默认是栈分配 (说人话就是出了作用范围就自动释放)
那么当我们想给其他线程里 丢数据的时候
如果用易语言的想法 就得 申请内存 然后写入内存 传内存地址给线程 线程内在按照规则读取回来
当我们有了类型指针
就可以
[Pascal] 纯文本查看 复制代码 //线程函数1
procedure ThreadWork(Param: Pointer);
begin
var student := pStudent(Param)^;
Form1.Memo1.Lines.Add(Student.Name + '=' + Student.Age.ToString);
Dispose(pStudent(Param));
//将这个指针释放 需要先将他转成类型指针才能让编译器知道指针大小
end;
//线程函数2
procedure ThreadWork2(Param: pStudent);
begin
var student := Param^;
Form1.Memo1.Lines.Add(Student.Name + '=' + Student.Age.ToString);
Dispose(Param); //将这个指针释放
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
{前面说了 ^TStudent 其实就表示是一个指向Tstudent类型的指针,自然就包含了他该有的类型信息(内存布局)}
var pS: pStudent;
{给pStudent 在堆上给他申请了匹配的内存}
New(pS);
//所以这一步 其实就是 AllocMemory(sizeof(TStudent))然后执行 结构数据初始化 这2个过程
//运行到这里 pStudent 其实已经指向了 实际的内存中因为上一步我们给他分配了,分配之前他只是个类型描述而已
pS.Name := '孙悟空'; //本质就是 pStudent^.Name 可以不写^的原因是 编译器给我们做了
pS^.Age := 99;
//这两句 其实就是给pStudent指向的内存中写入了 数据
var Thid: TThreadID;
var Handle:=BeginThread(nil, 0, @ThreadWork2, pS, 0, Thid);
CloseHandle(Handle);
end;
线程函数1 和 线程函数2 明显2 更易懂 推荐用。
另外Delphi中已经将线程默认抽象成一个类 所以可以直接往类里加成员 实现数据传递
|