Codesys数据结构:扩展数据类型之指针 Pointer 详解

本文详细介绍了Codesys编程环境中关于指针的数据结构,包括地址描述、绝对地址的概念和表示,以及指针的定义、操作和应用。通过关键词'Pointer to'、'adr'和'^'解释了如何使用指针读取和修改变量值,并讨论了指针的类型大小、地址映射、指针保护和指针偏移。示例中展示了如何利用指针优化数组求和的函数实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 地址描述

在内存中,每个字节都有一个编号,通过编号找到存储区(类似去1-407室 取快递,编号就是1-407,快递就是对应存储区),这个编号在这里我们取个名字叫做地址;当定义一个变量并为其赋值的时候,计算机会开辟一块空间用来存取变量值,在程序中一般是通过变量名来引用变量的值,实际上程序在编译的时候已经将变量名字转为变量的地址,即通过变量名找存储单元的地址,从而读取或修改存储单元的变量值;
在这里插入图片描述

2 绝对地址

(1)描述

在设备中比如PLC,已经提前定义好一部分地址的用途(比如地址(16#0080000~~16#008FFFF)只用于外部输入地址),
在Codesys中 I 、Q 、 M 表示设备的绝对地址区域,分别是输入地址区、输出地址区、中间地址区;

(2) 地址表示

格式:%<内存区域>(<大小>)<内存位置>

  • 内存区域 : I 、Q、M
  • 大小 : X(位)、B(字节) 、W(字)、D(双字)
  • 内存位置:数字

例如 下面的关系对照表

举例Value
%IB1输入区域B1的地址;
%IX1.2输入区域B1中位2的位地址;
%QD2输出区域D2的地址;
在这里插入图片描述
(3) 地址映射

%<内存区域>(<大小>)<内存位置> :只是表示从哪个地址开始取,具体取多少个字节,是由我们定义的数据类型决定;

dwTest AT %IB1 : DWORD;

表示 从输出区域字节B1开始取 4个字节(DWORD占四个字节)中的内容映射到dwTest变量中;

3 指针

(1)描述

指针就是地址,它可以是绝对地址,也可以是用户自定义变量的地址;
当定义指针的时候,必须定义指针的数据类型,因为指针指向的变量类型不同,而类型所占字节大小不同,所有如果仅仅知道地址不知道类型时,指针就不知道从当前地址读取多少个字节;

(2)关键字:“Pointer to” 、“adr” 、“^”
  • “Pointer to” : 用来定义指针类型变量;
piTest: pointer to dword;(*指向dword类型的指针*)
pbTest: pointer to bool;(*指向bool类型的指针*)
prTest: pointer to real;(*指向real类型的指针*)
paiTest: pointer to array[1..10] of int ;*指向int类型数组的指针*)
apiTest: array[1..10] of pointer to int;(*int类型指针数组:存储10个指针,每个指针指向Int类型*)
  • “ADR” 用来获取变量的地址
  • “^” 用来读取指针变量中地址对应的数值
VAR
piTest : POINTER TO INT;
iTest  : INT := 1; 
iTest2 : INT := 0;	
END_VAR

piTest := ADR(iTest);(*获取变量piTest的地址,保存到piTest指针变量中*)
iTest2 := piTest^;(*取地址对应的存储单元数据,赋值给iTest2*)

在这里插入图片描述

(3)指针变量中值的含义
  • 每个地址对应一个byte存储单元,即每个byte都有一个独立的地址
  • WORD\INT 等16位数据类型包含2个byte,其地址用第一个byte的地址表示;
  • dowrd\dint等32位数据类型包含4个byte,其地址用第一个byte的地址表示,以此类推;
  • 指针变量中的值代表某一个byte地址,指针的类型决定了是指向以这个byte开始,连续多少个字节;

在这里插入图片描述

  • 图中1000指的是第一个byte的地址,1001指的是第二个byte的地址,以此类推;

(1) 每个地址对应的空间是一个byte,byte中的每一位bit不具备单独的地址,如下图所示,取输出地址区第1个字节中每一位的地址,运行查看都是同一个值,这个值就是这个字节的地址;

VAR
	bBool_0 AT %QX0.0 : BOOL;
	bBool_1 AT %QX0.1 : BOOL;
	bBool_2 AT %QX0.2 : BOOL;
	bBool_3 AT %QX0.3 : BOOL;
	bBool_4 AT %QX0.4 : BOOL;
	bBool_5 AT %QX0.5 : BOOL;
	bBool_6 AT %QX0.6 : BOOL;
	apBool : ARRAY[0..7] OF POINTER TO BOOL;//指针数组
END_VAR

apBool[0] := ADR(bBool_0);
apBool[1] := ADR(bBool_1);
apBool[2] := ADR(bBool_2);
apBool[3] := ADR(bBool_3);
apBool[4] := ADR(bBool_4);
apBool[5] := ADR(bBool_5);
apBool[6] := ADR(bBool_6);

在这里插入图片描述

  • bBool AT %QW0 :BOOL;
    对于布尔变量,如果未指定单个位地址,则会在内部分配一个字节。bBool的值更改会影响从QW0.0到QW0.7的范围。
  • bBool AT %QW0.1 :BOOL ;
    布尔变量声明,其中明确指定了单个位地址。bBool只影响QW0.1的值
(4)指针类型大小

指针类型大小为8个字节,和指向的数据类型无关,如下图所示;所以指针类型变量可以表示(2^64-1) 中不同的数,每一个数代表一个byte地址,也就是可以表示(2^64-1) 个byte内存空间大小;

VAR   
piTest: POINTER TO DWORD;(*指向dword类型的指针*)
pbTest: pointer to bool;(*指向bool类型的指针*)
prTest: pointer to real;(*指向real类型的指针*)
paiTest: POINTER TO ARRAY[1..10] OF INT ; (*指向int类型数组的指针*)
iSize  :ARRAY[0..3] OF  UINT;
END_VAR

iSize[0] := SIZEOF(piTest);
iSize[1] := SIZEOF(pbTest);
iSize[2] := SIZEOF(prTest);
iSize[3] := SIZEOF(paiTest);

在这里插入图片描述

sizeof 返回变量所占空间的字节数

(5)指针类型的地址

指针类型的变量也是变量,每个变量都有自己的地址;
如下图所示:

  • piTest中的值是dwTest的地址
  • ppTest中的值是piTest的地址;
  • ppTest^ : 获得的是dwTest的地址
  • ppTest^^ : 获得的是dwTest变量的值,等价于 piTest^
VAR
ppTest: POINTER TO POINTER TO DWORD;  
piTest: POINTER TO DWORD;
dwTest : DWORD := 100;
END_VAR

piTest := ADR(dwTest);
ppTest := ADR(piTest);

在这里插入图片描述

在这里插入图片描述

(6) 地址映射
  • 指针的值(变量的地址)可以知道从那块区域取数据
  • 指针的类型可以知道取多少个字节数据;
  • 取的数据要和指针的类型一一映射

首先定义一个结构体

TYPE DUT :
STRUCT
	bTest1 : BOOL;(*1个字节*)
	bTest2 : BOOL;(*1个字节*)
	iTest  : INT;(*2个字节*)
	diTest : DINT;(*4个字节*)
END_STRUCT
END_TYPE

进行测试

VAR
 abyTest :ARRAY[0..7] OF BYTE := [1,0,2,0,7,0,0,0];
 pdutTest: POINTER TO DUT;
END_VAR

pdutTest := ADR(abyTest[0]);

在这里插入图片描述
其原理如图,变量pdutTest的值是 abyTest[0]的地址,然后就从abyTest[0]开始,取pdutTest指向DUT数据类型大小的数据,DUT为8个字节;也就是取abyTest[0]到abyTest[7]中的内容(因为数组的物理地址是连续的)映射到结构体DUT中;
在这里插入图片描述

(7) 指针保护
  • 指针没有保护,如果指针指向的位置是不可知的,因为指针变量的值是别的变量的地址,通过指针访问修改了一个不确定含义的地址,结果是不可知的;
VAR
 pdutTest: POINTER TO INT;
END_VAR

pdutTest :=16#002341234;(*随便赋值,16#002341234地址不知是哪个变量地址*)
  • 指针默认值是为0,意味着空指针
VAR
 pdutTest: POINTER TO INT;
END_VAR

在这里插入图片描述

  • 指针没有类型保护
    例如用一个INT类型指针,指向BOOL变量地址不会报警。
    一个BOOL变量会占用一个字节空间,其变量值取决于字节最低位的值;如果修改其他位(比如第二位)的值为1,BOOL变量会怎么样呢 ?
    在这里插入图片描述
    代码验证
VAR
 bTest  : BOOL:= TRUE;
 pbyTest: POINTER TO INT;
END_VAR

pbyTest := ADR(bTest);(INT型指针 指向BOOL类型地址)
pbyTest^ := 2;(*通过指针,把BOOL变量第二位置为1*) 

在这里插入图片描述

(8) 指针偏移:下标([])和加减操作符,可以控控制指针的偏移

(1)加减操作符方式

VAR     
  piTest : POINTER TO INT;
  aiTest : ARRAY[1..10] OF INT :=[1,2,3,4,5,6,7,8,9,10];
  iTest  : INT;
END_VAR

(*获得数组 aiTest[0]的地址,等价于piTest := ADR(aiTest[0])*)
piTest := ADR(aiTest);
(*获得数组 aiTest[1]的地址,需要当前地址偏移2个Byte地址,因为一个INT占2个byte*)
piTest := piTest + 2;
iTest  := piTest^;

在这里插入图片描述

(1)通过下标的方式:它进行了2部分工作,第一步是进行地址偏移,第二步取偏移后地址对应存储区的变量值;

VAR     
  piTest : POINTER TO INT;
  aiTest : ARRAY[1..10] OF INT :=[1,2,3,4,5,6,7,8,9,10];
  iTest  : INT;
END_VAR

(*获得数组 aiTest[0]的地址,等价于piTest := ADR(aiTest[0])*)
piTest := ADR(aiTest);

(*1 地址偏移:地址偏移量的计算方法是 下标中的数字n * sizeof(piTest所指向的数据类型),在这里是  1 * sizeof(int) = 2*)
(*2 取新地址对应存取区的变量值*)
iTest := piTest[1];

在这里插入图片描述

(9) 应用举例

写一个求可变数组求和的函数(数组是可变,最大数组长度位10000,最小为1)

  • 写法1:
FUNCTION POU : LINT
VAR_INPUT
	aiTest : ARRAY[1..10000] OF INT ;(*传入外部数组所有数据*)
    iNum   : INT;  (*外部数组长度*)
END_VAR
VAR
  i :INT;
END_VAR

(*求和计算*)
FOR i := 1 TO iNum BY 1 DO
    POU := POU + aiTest[i];(*累加求和*)
END_FOR;
  • 写法2:–指针
FUNCTION POU : LINT
VAR_INPUT
	aiTest : POINTER TO INT ;(*传入外部数组首地址*)
    iNum   : INT;  (*外部数组长度*)
END_VAR
VAR
  i :INT;
END_VAR

(*求和计算方式1*)
FOR i := 1 TO iNum BY 1 DO
    POU := POU + aiTest[i]; (*取地址对应中的数据,不断累积求和*)
END_FOR;

(*求和计算方式2*)
FOR i := 1 TO iNum BY 1 DO
    POU := POU + aiTest^; (*取地址对应中的数据,不断累积求和*)
	aiTest := aiTest + SIZEOF(INT);(*指向数组中下一个INT型变量地址*)
END_FOR;


  • 比较说明
    写法2明显会开辟的空间小,指针类型变量占用8个字节,数组变量(ARRAY[1…10000] OF INT)占用10000*2=20000个字节,如果数组长度为百万,千万,其效果更明显;
    请添加图片描述
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值