我的问题是:使用指针和引用有什么好处?
我是 codesys 的新手,在之前的工作中,我在 TIA Portal(西门子)和 Sysmac Studio(欧姆龙)中进行编程,从未遇到过指针或类似的东西。我想我了解它们的工作原理,但不确定我自己何时应该使用它们。
例如,我刚刚从供应商那里收到了一个功能块:
为什么他们不只用一个数组来输入和输出?
我现在能想到的主要有6个:
类型双关语,您也可以使用
UNION
来实现,但您可能不希望为代码中的每个重新解释转换创建一个联合。
节省内存和复制执行时间。每当您将一些数据传递给函数/功能块时,它都会被复制。如果您的 PLC 有足够的 CPU 能力和内存,这不是一个大问题,但是如果您在低端 PLC 上处理特别大的数据,那么您可能会超出实时执行限制,或者耗尽内存。然而,当您传递指针/引用时,无论数据有多大,都只会复制并传递指针/引用,在 32 位系统中为 4 个字节,在 64 位系统中为 8 个字节。
顺便说一句,由于 CODESYS 始终将使用接口类型声明的变量视为引用。,如果您一直将功能块作为接口传递,那么您已经在使用引用了。
如果您想传递一些数据并在函数内部对其进行修改,您可以返回修改后的数据的副本并覆盖原始数据,或者只是通过引用/作为指针传递它。 Codesys 通过使用 VAR_IN_OUT
来简化这一过程,它实际上只是底层的
REFERENCE TO
,所以如果您曾经使用过它,那么您就已经使用了引用。
有多个输出,例如:
VAR_OUPUT
out1 : INT; (*1st output variable *)
out2 : INT; (*2nd output variable *)
//...
END_VAR
假设我们想要一个功能块来计算给定数字系列的
移动平均值。一个简单的方法是这样的:
FUNCTION_BLOCK MyMovingAvg
VAR_INPUT
nextNum: INT;
END_VAR
VAR_OUTPUT
avg: REAL;
END_VAR
VAR
window: ARRAY [0..50] OF INT;
currentIndex: UINT;
END_VAR
但是,这存在一个问题,即移动窗口大小是静态的且是预定义的。如果我们想要不同窗口大小的平均值,我们要么必须为不同窗口大小创建多个功能块,要么执行如下操作:
FUNCTION_BLOCK MyMovingAvg
VAR CONSTANT
maxWindowSize: UINT := 100;
END_VAR
VAR_INPUT
nextNum: INT;
windowSize: UINT (0..maxWindowSize);
END_VAR
VAR_OUTPUT
avg: REAL;
END_VAR
VAR
window: ARRAY [0..maxWindowSize] OF INT;
currentIndex: UINT;
END_VAR
我们只使用数组中从 0 到
windowSize
的元素,其余的将被忽略。然而,这也存在问题,我们不能使用大于
maxWindowSize
的窗口大小,并且如果 maxWindowSize
设置得很高,可能会浪费大量内存。有两种方法可以获得真正通用的解决方案:
使用动态分配。然而,正如我之前提到的,并非所有 PLC 都支持此功能,默认情况下禁用它,有缺点(您必须将内存分成两个块),几乎不使用并且不太像 CODESYS。
FUNCTION_BLOCK MyMovingAvg
VAR_INPUT
nextNum: INT;
END_VAR
VAR_OUTPUT
avg: REAL;
END_VAR
VAR
windowPtr: POINTER TO INT; // pointer to the first array element. You can access other elements with [] as usual (up to windowSize)
windowSize: DINT;
currentIndex: UINT;
END_VAR
METHOD FB_Init: BOOL
VAR_INPUT
bInitRetains: BOOL;
bInCopyCode: BOOL;
END_VAR
VAR_IN_OUT // basically REFERENCE TO
window_buffer: ARRAY [*] OF INT; // array of any size
END_VAR
THIS^.windowPtr := ADR(window_buffer);
THIS^.windowSize := UPPER_BOUND(window_buffer, 1) - LOWER_BOUND(window_buffer, 1) + 1;
// usage:
PROGRAM Main
VAR
avgWindow: ARRAY [0..123] OF INT; // whatever size you want!
movAvg: MyMovingAvg(window_buffer := avgWindow);
END_VAR
movAvg(nextNum := 5);
movAvg.avg;
同样的原理可以应用于任何对数组进行操作的功能块(例如,我们也用它来排序)。更新
:Codesys 引入了 VAR_GENERIC CONSTANT,您基本上可以在功能块中定义常量输入,例如 arraySize
,并用它定义输入数组,从而降低上述方法的实用性。