Dereference the pointer with the caret before the member name: Measurement^.MaxValue := 5.0 and Measurement^.History[3] := 1.0 are the correct forms, the array index follows the member, and ADR(Measurement^.MaxValue) takes a field's address.
You declared the function block input as Measurement : POINTER TO sHisto, and now Measurement.History[3] := 1.0; refuses to compile. The rule that fixes every line in the block: the caret dereferences the pointer, and it goes between the pointer name and the member name. Write Measurement^.History[3] := 1.0; and Measurement^.MaxValue := 5.0;, and the reads use the same path. This page works through the whole interface: where the array index goes, how to take a member's address with ADR, how to hand the pointer to another block, and what to check when the syntax is right but the numbers are wrong. The examples use a history structure with one array of 64 REAL values and three scalar members, the shape you meet in measurement and test logging blocks.
How do I reference struct members through a pointer in structured text?
Dereference first, select the member second: Measurement^.MaxValue := 5.0; writes the REAL field through the pointer, and temp := Measurement^.MaxValue; reads it back, in CODESYS and the other IEC 61131-3 tools that share this syntax.
A pointer stores an address. POINTER TO sHisto in the interface tells the compiler what lives at that address, the caret follows the address and yields the structure, and the member name then selects one field of that structure. Read the declaration before writing any access: an input declared POINTER TO sHisto takes Measurement^.member at every use, and an input declared POINTER TO ARRAY or POINTER TO REAL takes that target type's access instead.
The caret belongs to the CODESYS family of IEC 61131-3 development tools. Controllers outside that family reach members through their own mechanisms, and pointer access to struct members in multi-instance data blocks shows how one major platform does it.
Why does Measurement.History[3] fail while Measurement^.History[3] compiles?
The pointer holds an address, and a member can be selected only from the structure value, so the caret must come first; Measurement.MaxValue^ fails for the mirror-image reason, because MaxValue is a REAL and a REAL has nothing to dereference.
| Expression | Compiles | Why |
|---|---|---|
| Measurement^.MaxValue := 5.0; | Yes | Writes the REAL member through the dereferenced structure |
| temp := Measurement^.MaxValue; | Yes | Reads the same member into temp |
| Measurement^.History[3] := 1.0; | Yes | Writes element 3 of the array member |
| Measurement.History[3] := 1.0; | No | Selects a member from an address, with no structure in hand |
| temp := Measurement.MaxValue^; | No | Applies the caret to a REAL value, which has no address to follow |
The read and the write share one member path: whichever side of := the member sits on, the expression is identical. The two failing forms each put the caret where no pointer exists. In Measurement.History[3] the dot tries to select a member from an address. In Measurement.MaxValue^ the caret tries to dereference a REAL. The compiler rejects both with a type or operand message that reads oddly until you look at where the caret sits.
Where does the array index go when the member is an array?
After the member name: the caret yields the structure, .History yields the array, and [3] then selects one element, so the whole write is Measurement^.History[3] := 1.0; with the index inside the declared bounds of 1 to 64.
Member first, index second. The caret yields the structure, .History yields the whole array inside it, and only an array accepts an index. Measurement^[3] fails the same way as Measurement.History[3], because at that point in the expression there is no array to index.
Then hold the index to the declared type. History is ARRAY [1..64] OF REAL, so the first element is History[1], the last is History[64], and History[0] lies outside the type. A loop over the member runs from 1 to 64, and an index carried over from zero-based habits writes outside the array on its first iteration.
How do I take the address of one member or of the whole array?
Apply ADR to the already-dereferenced member: ADR(Measurement^.MaxValue) is the address of the REAL field, ADR(Measurement^.History) is the address of the whole 64-element array, and ADR(Measurement) is the address of the pointer variable itself.
| Address you need | Expression | Receiver declares |
|---|---|---|
| One scalar member | ADR(Measurement^.MaxValue) | POINTER TO REAL |
| One array element | ADR(Measurement^.History[1]) | POINTER TO REAL |
| The whole array member | ADR(Measurement^.History) | POINTER TO ARRAY [1..64] OF REAL |
| The pointer input itself | ADR(Measurement) | POINTER TO POINTER TO sHisto |
ADR goes on the expression that names the object whose address you want. Applied to a dereferenced member it reaches inside the structure. Applied to the pointer variable it answers a different question, the address of the input slot, and its only use is a pointer to a pointer. A pointer to the whole array member and a pointer to one of its elements are different types, and the receiver's declaration decides which address to take.
How do I pass the structure pointer on to another block?
Assign the pointer straight across: SomePointerTosHisto := Measurement; copies the structure's address into a second pointer declared POINTER TO sHisto, with no ADR call and no caret, because the address is already in hand.
At the caller the address is taken once, from the object:
VAR
m : sHisto;
END_VAR
fbStats(Measurement := ADR(m));
Inside the block, Measurement already holds the structure's address, so hand it on as it is and take no new address of it. The variable behind the address must live as long as the block holds it: aim the pointer at a caller variable that persists between scans, and refresh the assignment every scan when the source can change.
What does a worked example look like for one measurement block?
A caller declares m : sHisto and passes ADR(m); the block zeroes the 64 slots, writes 1.0 to slot 3 and 5.0 to slot 17, scans all 64, and finishes with MaxValue = 5.0, MinValue = 0.0 and AvgValue = 0.09375.
FUNCTION_BLOCK fbStats
VAR_INPUT
Measurement : POINTER TO sHisto;
END_VAR
VAR
i : INT;
sum : REAL;
END_VAR
FOR i := 1 TO 64 DO
Measurement^.History[i] := 0.0;
END_FOR
Measurement^.History[3] := 1.0;
Measurement^.History[17] := 5.0;
Measurement^.MaxValue := Measurement^.History[1];
Measurement^.MinValue := Measurement^.History[1];
sum := 0.0;
FOR i := 1 TO 64 DO
sum := sum + Measurement^.History[i];
IF Measurement^.History[i] > Measurement^.MaxValue THEN
Measurement^.MaxValue := Measurement^.History[i];
END_IF
IF Measurement^.History[i] < Measurement^.MinValue THEN
Measurement^.MinValue := Measurement^.History[i];
END_IF
END_FOR
Measurement^.AvgValue := sum / 64.0;
Every access goes through the caret, including the indexed ones inside both loops. The arithmetic at the end: the 64 slots hold 1.0 in slot 3, 5.0 in slot 17 and 0.0 in the other 62, so sum = 1.0 + 5.0 = 6.0 and AvgValue = 6.0 / 64.0 = 0.09375. MaxValue takes 5.0 from slot 17 and MinValue takes 0.0 from the empty slots. The member expressions that pass the compile are the same ones you reuse downstream, to display PLC values in a visualization or to log PLC variables to a database.
Can I reach struct members by indexing the pointer as an array?
Indexing a pointer steps in units of the target type, so it selects whole elements of what the pointer addresses; it never turns the named members of a structure into positions 0 and 1 of an array.
The tempting shortcut, declaring POINTER TO ARRAY [0..1000] OF STRING, aiming it at a structure and writing INIValues1^[0]^[0] := 'Test';, fails twice. The type left of POINTER TO decides what a subscript means, and a structure is not an array of strings. And structure members are named fields, reached by name through a pointer declared to that structure type; there is no element 0 to write through.
When the target really is a string array, declare the pointer to the element type and let the subscript do the dereferencing:
VAR
Values : ARRAY [0..1000] OF STRING;
pValue : POINTER TO STRING;
END_VAR
pValue := ADR(Values);
pValue[0] := 'Test';
On a pointer, a subscript reads one element of the target type, so pValue[0] and pValue^ name the same string. That is exactly the behavior you want over an array, and exactly why the pointer's declared type must match the object it addresses.
What should I check when the syntax is right but the data is wrong?
Check three things in order: the assignment that filled the pointer, the index against the declared bounds of the array member, and the target type of every pointer that received an ADR result; most faults sit in one of those three.
Work the list in this order:
- Trace the assignment. The pointer must be filled from a live sHisto variable at the caller, and the block should verify it holds an address before the first dereference.
- Hold every index to the declared bounds of the array member; the runtime catches a bad index when bounds checking is enabled in the build.
- Match every ADR destination to what was addressed: POINTER TO REAL for a member or an element, POINTER TO ARRAY for the array member.
- Recompile the whole project after any change to sHisto, so every access through every pointer is built against the current layout.
When a pointer fault appears only after hours of running, the address has gone stale and the diagnosis has its own path; PLC access violation troubleshooting for sporadic runtime faults walks it.
Recommendation: keep the interface as POINTER TO sHisto and write every access as Measurement^.member, with the index after the member and ADR applied to the dereferenced member when a callee needs an address; when you own both the caller and the block, change the input to a VAR_IN_OUT of type sHisto and the same reads need no pointer syntax at all.
Numbers like MaxValue and AvgValue usually end up in front of people. Whether they belong on the HMI page or on a separate board the whole line can read is its own decision, and live factory data display, signage or dashboard makes it; the standalone side of that choice is what our shop-floor screens are for.
Questions
What does Measurement[1] do when Measurement is a pointer to a structure?
A subscript on a pointer counts in units of the target type, so Measurement[1] means the structure one sHisto-sized step past the addressed one. That is the right tool when the pointer aims at the first element of an array of structures, and wrong against a single structure variable, where it reads past the object into whatever follows in memory. The subscript selects whole elements of the target type, never parts of one.
How do I guard against dereferencing an empty structure pointer?
Compare the pointer with 0 before the first access, an IF Measurement = 0 THEN RETURN; END_IF guard at the top of the block. A dereference through address 0 raises an access violation and stops the task, so the guard costs one line and saves a stoppage. It belongs in every block whose pointer input comes from a caller outside your own code.
Would a REFERENCE TO sHisto be simpler than a pointer?
Usually yes. A reference binds to one object and is dereferenced implicitly, so the member access reads histoRef.MaxValue with no caret, and the array member reads histoRef.History[3]. The trade is flexibility: a reference is bound at its assignment and is then used as the object itself, while a pointer can be passed on, compared with 0, or re-aimed at another structure. For a fixed one-to-one wiring between caller and block, the reference reads cleaner.
Planning information, not legal or engineering advice. Rules differ by place: check codes and permits with your local authority and your licensed trades.