Skip to content

24Structured programs

References and pointers

What REF_TO, POINTER TO, ADR and dereferencing mean, what aliasing does to your debugging, and why none of it belongs in plant code.

Lesson 24 of 3410 minIEC 61131-3

An address instead of a value

Everything so far has dealt in values: you copy a number into a variable. Pointers and references deal in addresses: a variable that holds where another variable lives in memory.

REF_TO

The third edition of the standard added the reference, a type-checked pointer:

VAR
  pTank : REF_TO T_Analog;
  LT101 : T_Analog;
END_VAR

pTank := REF(LT101);

IF pTank <> NULL THEN
  pTank^.Value := 42.0;
END_IF;
  • REF(x) takes a reference to x.
  • ^ dereferences it: "the variable this points at".
  • The NULL check before any use is not optional.

POINTER TO and ADR

The older form, found in CODESYS, TwinCAT and others, and more dangerous because nothing checks the type:

VAR
  p : POINTER TO REAL;
  x : REAL;
END_VAR

p  := ADR(x);
p^ := 3.14;

ADR hands you a raw address. Nothing stops you pointing a REAL pointer at an INT, reading meaningless bytes, or writing past the end of an array and corrupting the variable next door.

Aliasing

This is the real problem: one memory location with two names.

p := ADR(Setpoint);
p^ := 80.0;        (* Setpoint is now 80, and no line mentions Setpoint *)

You search the whole project for Setpoint := and find nothing, while the value keeps changing. Search does not help, code review does not help, and the watch table tells you the value moved but not who moved it. That is hours of diagnosis on a stopped plant.

Three reasons to keep them out of plant code

  1. They destroy readability. The strength of ST is that a line says what it does. A pointer breaks that: what changes is only knowable by following an address at runtime.
  2. They switch off the checks. The compiler stops you assigning an INT to a BOOL and catches an index outside its range. A raw pointer asks you to give that protection up voluntarily.
  3. They stop controllers. An uninitialised pointer, a reference to a local that no longer exists, an address outside the permitted area: either the CPU faults with an access violation, or other data is silently corrupted. The second is worse.

Add that pointers do not travel between vendors. The syntax and the semantics differ, so pointer-heavy code is locked to one platform.

Where they do belong

  • Inside a library you write once, test hard, and expose behind a clean interface.
  • In handling a large data block from a comms link where copying genuinely costs, after measuring that copying is the bottleneck.
  • Talking to devices or protocols that dictate a memory layout.

In station logic, in a motor program, in a sequence, in alarm handling: no.

What to use instead

  • VAR_IN_OUT when a block must update something large the program owns. The intent is explicit and the compiler stays on your side.
  • A function block with instances when you want the same logic over different data.
  • An array and an index when you want to pick an element at runtime. An index can be clamped and range-checked; an address cannot.

The rule

If you find yourself needing a pointer inside process logic, it is usually the design that needs changing, not the memory.

Try it in the lab

The lab runs in your browser: an ST editor, a virtual process that suits the lesson, the same program as a live ladder diagram, a watch table, and export to Siemens SCL.

What to try

  1. Run the program and turn S4 on: L1 lights because Index is 0.
  2. Turn S1 on, then S2, and watch Index: the command moves to L2, then L4.
  3. That is what a pointer with a computed address does, except CASE can never write outside the four lamps.

People find this lesson by searching

REF_TO structured textPOINTER TO plcADR operator codesyspointer dereference structured textaliasing plc codereference vs pointer IEC 61131-3avoid pointers plc

Course outline

  1. 01What is Structured Text, and when to use it instead of ladder
  2. 02How a PLC runs your code: the scan cycle and the process image
  3. 03Program structure: POUs, PROGRAM, FUNCTION and FUNCTION_BLOCK
  4. 04Your first program: starting a pump with seal-in logic
  5. 05Data types: BOOL, INT, REAL, TIME and the rest
  6. 06Declaring variables: VAR, VAR_INPUT and RETAIN
  7. 07Literals and constants: 16#FF, 2#1010 and T#5s
  8. 08Type conversion and scaling a 4-20 mA signal
  9. 09Assignment and arithmetic in ST: := and operator precedence
  10. 10Boolean logic, comparisons and bit operations
  11. 11Standard functions: ABS, SQRT, MIN, MAX, LIMIT, SEL, MUX
  12. 12IF, ELSIF and ELSE: the rules and when to avoid them
  13. 13The CASE statement and your first state machine
  14. 14The FOR loop and the watchdog
  15. 15WHILE, REPEAT, EXIT, CONTINUE and RETURN
  16. 16Edge detection: R_TRIG, F_TRIG and the "it fired every scan" bug
  17. 17Timers: TON, TOF and TP
  18. 18Counters: CTU, CTD and CTUD
  19. 19SR and RS flip-flops: which one wins
  20. 20Writing your own function
  21. 21Writing a function block
  22. 22Arrays in Structured Text
  23. 23Structs and enumerations
  24. 24References and pointers
  25. 25The state machine pattern: a batch sequence that cannot get stuck
  26. 26Alarm management to ISA-18.2: delay, deadband, latch, acknowledge, first-out
  27. 27Analogue signal processing: counts to engineering units, NE43 and filtering
  28. 28Closed-loop control in ST: hysteresis first, then a practical PID
  29. 29Motors and valves in practice: permissives, feedback and travel time
  30. 30The same ST on every platform: SCL, Studio 5000, CODESYS, TwinCAT
  31. 31Testing and debugging ST: the watch table, forcing and simulation
  32. 32An ST style guide: naming, comments, constants and version control
  33. 33Final project: a complete tank station with a state machine and alarms
  34. 3420 interview questions on ST and PLC programming, with answers