reference · South Africa
IEC Array Instructions: Bounds, Loops and Recipe Lookup
IEC array instructions for learners: understand bounds, dynamic indexing, FOR loops and recipe searches with missing IDs, duplicates and clear test cases.

IEC array instructions and array handling let a PLC program work with a collection of related values through an index. Recipes, channel readings and event records are common learning examples. The essential skill is to distinguish the storage position from the meaning of the item stored there, then check every access against the correct limits.
A useful array lesson goes beyond writing a FOR loop. You should be able to explain the array's capacity, which entries are populated, what an invalid index does in your chosen environment and what happens when a search finds no suitable record. Those questions apply whether you learn Structured Text, Siemens SCL or a vendor's graphical instructions.
This site has a commercial connection to PLC Simulator. Its educational exercises do not guarantee support for arbitrary native array code or reproduce every controller's fault handling. The images here are conceptual learning illustrations, not screenshots of vendor software or evidence of production use.
Array bounds, length and valid records are different
An array declaration defines its element type and available positions. A count describes how many items you are considering. An upper bound is the highest permitted index in a particular dimension. These values sometimes look similar, especially in small examples, but substituting one for another causes errors.
For a fictional array whose indices run from 10 through 14 inclusive, the capacity is five elements. Its lower bound is 10 and its upper bound is 14. If only indices 10 through 12 contain usable records, the populated count is three. Neither five nor three is a valid replacement for the upper bound in that declaration.
| Property | Value in the example | What it describes |
|---|---|---|
| Lower bound | 10 | First available storage index |
| Upper bound | 14 | Last available storage index |
| Capacity | 5 | Number of available positions |
| Populated count | 3 | Number of records currently in use |
| Last populated index | 12 | Last used position under this contiguous-storage rule |
The capacity calculation is upper bound minus lower bound plus one. The last populated index is lower bound plus populated count minus one only when records are stored contiguously from the lower bound and the count is positive. For an empty collection, handle the zero-count case explicitly before forming or using a last-record index.
A different design might allow unused holes and maintain a Valid field for each entry. That design needs different iteration rules. Write the storage convention into the requirement instead of assuming that every allocated element contains a current recipe.
How declaration conventions vary by platform
Siemens documents ARRAY bounds and element types in its STEP 7 array reference. Its description covers fixed limits and variable-bound block parameters, subject to the documented platform conditions. The written lower and upper limits are part of the declaration; do not assume that every Siemens array starts at zero.
In Logix Designer, distinguish a type declaration such as DINT[10] from an access such as MyList[5]. The first describes an array with ten elements; the second selects an element. Rockwell's indexing reference illustrates both fixed indices and indices supplied through tags or expressions.
A declaration copied from one environment may need different syntax or configuration in another. Keep a short environment record containing the engineering application, version, target and relevant data declarations. This makes it possible for an instructor to reproduce the exercise without guessing which language dialect you meant.
Our Structured Text learning guide introduces textual logic. For array work, add explicit notes about the permitted index range, element type and populated-record convention to every example you save.

Dynamic indexing: validate the final value
A fixed access selects a known position. A dynamic access calculates or reads the position at runtime. If a program uses Position plus Offset as a subscript, it must validate that final result, not merely check Position while ignoring Offset.
For the 10-to-14 example, Position 12 with Offset 2 selects 14 and is within the allocated bounds. Position 12 with Offset 3 produces 15 and must be rejected by the example's access rule. A negative offset can fail at the lower end in the same way. Also consider whether the arithmetic itself fits the chosen index type before using its result.
Perform the validity decision before dereferencing the array. A readable approach is a separate conditional branch that checks the range and only enters the access branch when valid. Do not assume that combining a bounds test and an array access in one Boolean expression guarantees that the access will be skipped; expression-evaluation rules are platform-specific.
If an operator or another task can change an index, define how the access obtains a consistent value. Checking one value and later using a different value undermines the check. A local captured value can be part of the design, but the actual concurrency and data-transfer rules still depend on the target.
Worked example: find a recipe by ID
This is a fictional software exercise. It selects a candidate record from a small table; it does not authorise applying a recipe to physical equipment. The wider application would need its own validation, permissions and process-state checks before an operational change.
Use an allocated array with indices 10 through 14. In the first fixture, all five positions are populated. Each record has an ID and a simple label. The IDs deliberately differ from their storage positions.
| Storage index | Recipe ID | Label |
|---|---|---|
| 10 | 210 | Training A |
| 11 | 450 | Training B |
| 12 | 120 | Training C |
| 13 | 900 | Training D |
| 14 | 330 | Training E |
Requesting recipe ID 900 should identify the record at index 13. Accessing Array[900] would confuse an identifier with a position and fall outside this fixture's storage bounds. Even an ID that happens to resemble a valid index should be looked up according to the stated data model.
Define three possible search results: unique match, no match and duplicate match. The search returns a candidate only for a unique match. It leaves the active recipe unchanged in all cases; selecting a candidate is a separate operation from applying it. This separation makes failure behaviour easier to test.
The training algorithm scans all populated records and counts matches. It remembers the matching position only as a candidate until the search is complete. Exactly one match succeeds; zero or more than one produces the corresponding status without an accepted candidate.
First match is different from a unique match
A first-match search can stop at the first matching record if the requirement allows it. A unique-match search cannot assume uniqueness merely because one match has appeared. It must either inspect the rest of the relevant records or rely on a separately established uniqueness constraint.
With the five-record fixture, a first-match search needs one ID comparison for 210, four for 900, five for 330 and five for an absent ID such as 777. Our unique-match algorithm deliberately performs five comparisons for each request because it completes the full scan. These are comparison counts, not measured milliseconds or a promise about a controller's scan time.
| Request | Unique-match result | Candidate index | Comparisons in this full-scan model |
|---|---|---|---|
| 210 | Unique | 10 | 5 |
| 900 | Unique | 13 | 5 |
| 330 | Unique | 14 | 5 |
| 777 | No match | None | 5 |
For the duplicate fixture, change the ID at index 14 from 330 to 900. A request for 900 now produces two matches and no accepted candidate. It must not silently use whichever matching record appears first or last. That rule is a deliberate requirement of this exercise, not a universal requirement for every array search.
For an empty table, the search performs no record comparisons and returns no match. For partially populated storage, search only the populated portion under the declared contiguous-record convention. Do not let an old value in unused storage become a valid recipe simply because it still occupies memory.

Preserve failure information instead of stale success
Start each new search with a fresh result status and match count. If a previous request found index 13 and the next request fails, leaving an old Found flag true can make the application act on stale information. A candidate index without a validity status is easy for a later consumer to misuse.
Give the result enough context to identify the request it answers. In a simple classroom program, the requested ID and a completion indication may be sufficient. A longer-running search may need a request identifier and a data version so that the consumer can distinguish an old result from a new request.
An active recipe can remain unchanged after a failed selection while the interface clearly reports that failure. Those behaviours are compatible. Avoid displaying “recipe loaded” just because a previously active recipe is still present. The operator-facing message should describe the result of the current request.
Our PLC recipe-management guide develops the difference between stored parameters, validated selection and application. The array lookup here supplies only one part of that workflow.
Bounds checking is not the same as choosing the right record
A runtime can help detect or handle invalid indices, but staying inside memory bounds does not prove that the selected record is correct. Choosing index 14 instead of 13 can be perfectly within bounds while selecting the wrong recipe. Tests need to check semantic results as well as the absence of faults.
The CODESYS CheckBounds documentation describes an implicit-check function with a suggested implementation that limits an invalid index to a boundary. It also shows alternative handling using exceptions. The configured implementation matters; do not describe every CODESYS project as having identical behaviour.
For this recipe exercise, silently substituting a boundary entry is not an acceptable selection policy. A requested position of 15 should not become a successful selection of entry 14 merely because a monitoring function returned that boundary. Application validation needs to preserve the invalid-request result.
Rockwell documents a major fault of type 4, code 20 for the described element-oriented array instructions when a subscript exceeds its corresponding dimension. Treat that as a scoped statement from the indexing documentation, not a blanket claim about every raw-memory instruction, firmware revision or fault-recovery configuration.
For a Siemens project, use the error behaviour documented for the actual CPU and access instruction. Avoid copying an OB number or STOP rule from a different controller family. Test deliberate bounds failures only in an appropriate isolated training environment, with the expected recovery procedure established for that environment.
FOR loops need an execution budget
A loop can examine many records during one invocation. Its effect on execution time depends on the number of iterations and the work inside each iteration. Counting comparisons helps explain the algorithm, but it does not account for every string operation, record copy, instruction or scheduling effect.
There is no universal record count at which every PLC application should switch to a hash table or binary search. A binary search depends on an appropriate ordering rule, and maintaining that order has a cost. A direct lookup table can be useful when IDs and storage are intentionally mapped, but our fictional IDs are not array indices.
Select the simplest approach that meets the documented requirement and measured execution budget on the target. Include worst-case inputs: a missing ID can require the full search, while a match at the first position may make a first-match implementation look deceptively quick. A test containing only early matches is incomplete.
Choose a loop counter type that can represent the intended bounds and the values required by the loop's execution rules. Check the compiler's handling of boundary values and increments. Do not rely on a guessed wraparound behaviour to explain every possible loop failure.

Splitting a search across scans changes its contract
For a larger exercise, a designer might examine a limited number of records per invocation. That can distribute work, but it also creates a search that is in progress while the rest of the program continues. The result is no longer necessarily available when the first invocation returns.
For the five-record unique-match fixture, a budget of two records per invocation requires three invocations: two records, then two, then one. The final result is available only after the third completes. This count does not imply three milliseconds; the scheduling interval determines elapsed time.
Now consider what happens if another operation edits the table between those invocations. The search could combine records from different versions. Choose a consistency rule, such as searching a stable snapshot or detecting a version change and restarting the request. The copying or versioning mechanism itself needs to be appropriate for the target.
Also define cancellation and replacement. If a new ID arrives while the old search is incomplete, the application must decide whether to finish, cancel or queue. Resetting only the search index while retaining the old match count can combine two requests accidentally. Keep all request state together and test the transition explicitly.
The function block versus function reference helps decide where that ongoing search state belongs. A stateful operation can use an FB instance or caller-owned data; the important point is that ownership and lifecycle are explicit.
Multidimensional arrays and record copies
A multidimensional array has a separate valid range for each dimension. A valid row number does not compensate for an invalid column number. In a fictional table with row indices 1 through 3 and column indices 10 through 11, there are six positions, and every access needs both coordinates to be valid.
Keep the meaning of each dimension visible in names or comments. A value that is valid as a recipe index may still be wrong as a channel index. Where the application stores records with several fields, an array of a structured type can sometimes express the relationship more clearly than several parallel arrays.
When copying records or shifting an event history, check the actual instruction's length units, permitted types and overlap behaviour. Do not assume that a copy operation behaves like an overlap-safe move. Source and destination ranges can overlap even when their starting elements differ.
For example, shifting an event log towards higher indices by an ordinary forward element-by-element loop can overwrite a value before it is read for the next destination. A correctly designed reverse traversal, separate staging storage or a ring-buffer design addresses different requirements. Validate the chosen method rather than presenting a generic copy instruction as a universal one-line solution.

A useful array test plan
Begin with boundary access tests: the lower bound, upper bound, one below and one above. Add a computed index whose base is valid but whose offset makes the result invalid. Include the populated-length boundary as a separate test from the allocated-capacity boundary.
For searches, test the first record, last record, absent ID, duplicate ID and empty table. Start a failed request immediately after a successful request to expose stale flags. Verify both the returned status and the unchanged active recipe; checking only a candidate number is insufficient.
For a search divided across invocations, record the number of records inspected on each invocation and when completion becomes true. Test a table-version change, a cancelled request and a replacement request. Document whether those events restart the search or return a specific failure result.
Keep expected results independent from the implementation. A table written before execution is useful evidence. If a test fails, record the observed access or status and correct the implementation or clarify an ambiguous requirement. Do not quietly change the expected recipe to whatever the program happened to select.
PLC program testing practice can help develop that discipline. The checks accompanying this guide validate its fictional lookup and bounds models; they do not establish native controller execution or plant readiness.
What to ask in a South African PLC course
Ask whether learners create and inspect arrays themselves or only watch an instructor demonstrate them. A useful practical assessment should require a learner to distinguish an ID from an index, handle a missing result and explain an out-of-range request before running it.
For a TIA Portal, Studio 5000 or CODESYS course, confirm the actual software version, target and access arrangements. Ask whether the provider teaches native fault diagnostics and recovery on that target. General programming practice and platform-specific diagnostics are related activities, but one does not automatically prove the other.
Remote learners should establish whether they can save the project and test notes after the class, and whether access to the training environment continues. Classroom learners should confirm how much individual workstation time they receive. These details are more useful than a claim that a course covers every PLC instruction in a few hours.
A portfolio can show the fictional recipe fixture, expected outcomes, boundary tests and one corrected defect. Label the work as a training exercise and name the environment used. The Structured Text learning tools support earlier textual-logic practice; confirm exercise support before assuming arbitrary vendor array declarations can be pasted and executed.
Questions about PLC array indexing
Is array length the same as the last index?
Not generally. A zero-based array of five elements ends at index four. An array declared from 10 through 14 also contains five elements but ends at 14. Use the declared bounds and distinguish capacity from the number of populated records.
Can I use a scanned barcode as an array index?
Only if the application deliberately defines and validates that mapping. In the recipe example, the barcode represents an ID that must be searched for. Directly using ID 900 as a position would be incorrect for storage indexed from 10 through 14.
Should a failed lookup clear the active recipe?
That is an application requirement, not an array-language rule. This exercise leaves the active recipe unchanged and returns an explicit failure status. An operational application must define how that status affects the process and operator interface.
Does a successful bounds check prove the recipe is valid?
No. It establishes only that the index is within the range being checked. The record may be unused, duplicated, outdated or unsuitable for the requested operation. Validate those conditions separately.
Can a FOR loop wait for the next PLC scan?
Do not assume each loop iteration represents another scan. A multi-invocation search requires explicit saved progress and scheduling. Explain when the loop runs and when the result becomes complete in the actual environment.

Make the result easy to review
Save the array declaration, populated-record convention, search requirement and expected outcomes together. A reviewer should be able to identify why recipe 900 maps to index 13, why duplicate IDs fail and why the active recipe stays unchanged during selection.
That evidence demonstrates more than a loop that compiles. It shows that the learner understands valid storage, valid data and valid application behaviour as separate questions, and can connect them in a testable design.