S7-1200 Array Length in SCL: CountOfElements and Alternatives

David Krause18 min read
S7-1200SiemensTutorial / How-to
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

Overview

Determining the length of an ARRAY at runtime is a recurring requirement when writing SCL code for the SIMATIC S7-1200. Static dimensioning (ARRAY[1..10]) fixes the size at declaration time, but production code frequently needs the actual element count to iterate, validate, log, or build generic function blocks that accept arrays of any length. Unlike high-level languages (JavaScript arr.length, C++ sizeof(arr)/sizeof(arr[0]), Python len(arr)), SCL does not expose a .Length property on every ARRAY variable. Engineers must use one of several instructions from the basic/extended PLC program elements library: CountOfElements, SizeOf, LOWER_BOUND, UPPER_BOUND, SERIALIZE, or ATTR_DB. Availability depends on the CPU firmware and the TIA Portal version. This article walks through every working method, documents the firmware/portal constraints, and provides copy-paste-ready SCL snippets for the S7-1200.

The discussion is grounded in the SIMATIC S7-1200 programmable controller system manual and the SCL programming manual available on the Siemens Industry Online Support portal at support.industry.siemens.com. Whenever an instruction behaves differently across firmware revisions the relevant firmware cut-off is documented; whenever the behaviour diverges between the real CPU and PLCSIM the simulator revision is called out.

Prerequisites

  • SIMATIC S7-1200 CPU (any family member; FW 4.2 or newer recommended for the cleanest solutions).
  • TIA Portal V13 SP1 / V14 / V14 SP1 / V15 / V15.1 / V16 / V17 - version matched to the CPU firmware.
  • S7-1200 system manual - download via the Siemens Industry Online Support portal entry "SIMATIC S7-1200 Programmable Controller" (entry ID 109766459 is the canonical S7-1200 system manual page).
  • SCL programming manual "SIMATIC S7 SCL" - 03/2017 edition or later for the modern VARIANT-based APIs.
  • Optional: PLCSIM V15.1 or later for offline simulation of ARRAY[*].
  • Optional: Online diagnostic access to the CPU (Engineering Station > Online & Diagnostics) to verify firmware and diagnostic-buffer entries.
Firmware vs. TIA Portal compatibility. An instruction can be authored in a newer TIA Portal but it will not download onto a CPU whose firmware does not contain the corresponding block. Always check the S7-1200 firmware matrix published on the Siemens support portal (entry ID 109766459). A mismatch is the single most common cause of a successful TIA Portal compile that fails when downloading to the target.

The Anatomy of an ARRAY Variable in SCL

An ARRAY in SCL has three properties engineers usually need to recover at runtime:

  1. The lower index (e.g. 0 or 1 or -100).
  2. The upper index (e.g. 9 or 300 or 65535).
  3. The element count, equal to upper - lower + 1.

For multi-dimensional arrays, each dimension has its own lower and upper index. The total element count is the product of every dimension's length. The SCL DIM_COUNT_OF helper returns the number of dimensions of a static array expression.

ARRAY declarations in TIA Portal come in two flavours:

Syntax Description First FW on S7-1200
ARRAY[lo..hi] OF T Static, fixed at compile time All firmware
ARRAY[*] OF T Variable-length, the actual bounds come from the assigned block FW 4.2 (silicon 1AG40 / 2AG40 / 3AG40)

Method 1 - CountOfElements with VARIANT

CountOfElements was introduced on the S7-1500 platform and back-ported to selected S7-1200 CPUs starting with FW 4.2 on the 1AG40 / 2AG40 / 3AG40 silicon. The instruction accepts a VARIANT input and returns the element count as DInt. It counts one level of the array by default. When the input is a multi-dimensional array the result is the product of every dimension's length. For an array of STRUCT with three fields each, CountOfElements reports the number of STRUCT elements, not the number of scalar fields.

SCL skeleton for CountOfElements


// FB_GetArraySize
FUNCTION_BLOCK "FB_GetArraySize"
VAR_IN_OUT
  vArray : Variant; // caller wires any array here
END_VAR
VAR_OUTPUT
  size : DInt;
END_VAR
BEGIN
  // The instruction resolves the runtime type and returns element count
  #size := CountOfElements(variant := #vArray);
END_FUNCTION_BLOCK

Calling the FB from OB1


// OB1 - cyclic main
"FB_GetArraySize_DB"(vArray := "DataDB".MyArray);
"DB_Result".detected_count := "FB_GetArraySize_DB".size;
Why VARIANT? SCL's CountOfElements signature requires VARIANT because the block is generic - the same block should work for ARRAY OF INT, ARRAY OF REAL, ARRAY OF STRUCT and so on. Passing a typed array directly to a typed IN_OUT causes an implicit conversion error at compile time. The wrapper pattern above is the documented Siemens reference implementation.

Firmware coverage

CPU family First FW supporting CountOfElements Notes
S7-1500 FW 1.7 (all current FW) Full support, including multi-level
S7-1200 FW 4.0 / 4.1 Not supported Instruction greyed out in TIA Portal
S7-1200 FW 4.2 Supported Requires 1AG40 / 2AG40 / 3AG40 silicon
S7-1200 FW 4.3 / 4.4 / 4.5 Supported All variants
ET 200SP CPU FW 2.x Supported
S7-300 / S7-400 Not supported Use ATTR_DB / DB length arithmetic instead

Output interpretation

CountOfElements returns the number of top-level array elements. For ARRAY[0..9] OF INT it returns 10. For ARRAY[0..2, 0..4] OF REAL it returns 15 (3 x 5). For ARRAY[0..9] OF STRUCT {a:INT; b:REAL;} it returns 10 (10 struct elements, not 20 scalars). This semantic is consistent across S7-1500 and S7-1200.

Method 2 - SizeOf and Arithmetic

SizeOf returns the memory footprint in bytes of a variable or data type. With a VARIANT input, it reports the byte length of whatever structure is wired in. Divided by the element byte size, the element count can be derived. SizeOf works on every S7-1200 FW from 4.0 onward and on S7-1500 from the first release.

FC returning raw byte count


FUNCTION "FC_GetSizeBytes" : UDInt
VAR_IN_OUT
  vAny : Variant;
END_VAR
BEGIN
  FC_GetSizeBytes := SizeOf(variant := #vAny);
END_FUNCTION

FC returning element count by arithmetic


FUNCTION "FC_GetElementCount" : UDInt
VAR_IN_OUT
  vAny : Variant;
END_VAR
VAR
  totalBytes : UDInt;
  elemBytes  : UDInt;
END_VAR
BEGIN
  totalBytes := SizeOf(variant := #vAny);
  // Use a known element of the same type to compute per-element size
  elemBytes  := SIZEOF(INT);     // for ARRAY OF INT
  // elemBytes := SIZEOF(REAL);  // for ARRAY OF REAL
  // elemBytes := SIZEOF(BYTE);  // for ARRAY OF BYTE
  IF elemBytes > 0 THEN
    FC_GetElementCount := totalBytes / elemBytes;
  ELSE
    FC_GetElementCount := 0;
  END_IF;
END_FUNCTION

Edge cases

If the array contains STRING, WSTRING, STRUCT with variable-length members, or ARRAY OF BYTE used as a buffer, SizeOf returns the static declaration length, not the populated length. For populated-byte counting use SERIALIZE (Method 4). For true STRING content length use the LEN instruction on each element.

SizeOf also returns the declaration footprint for ARRAY OF BOOL with packed bits. TIA Portal packs booleans into the lower bit-count of a byte; the formula byteCount * 8 yields the bit-padded count, not always the number of named indices - check the declaration explicitly when debugging.

Method 3 - LOWER_BOUND and UPPER_BOUND

For a directly-typed ARRAY parameter (not a VARIANT), the LOWER_BOUND and UPPER_BOUND instructions return the lower and upper index of any given dimension. They accept the array name, the dimension number, and return DInt. The element count is then UPPER_BOUND - LOWER_BOUND + 1. These instructions require the array type to be visible at compile time, so they cannot resolve dynamic ARRAY[*], but they cover the common static case without a wrapper.

One-dimensional example


FUNCTION "FC_Bounds" : DInt
VAR_IN_OUT
  arr  : ARRAY[0..299] OF INT;
END_VAR
VAR
  lo   : DInt;
  hi   : DInt;
  dim  : Int := 1;
END_VAR
BEGIN
  lo := LOWER_BOUND(ARR := #arr,  DIM := #dim);
  hi := UPPER_BOUND(ARR := #arr,  DIM := #dim);
  FC_Bounds := hi - lo + 1;
END_FUNCTION

Multi-dimensional example


FUNCTION "FC_BoundsMulti" : DInt
VAR_IN_OUT
  arr  : ARRAY[0..2, 0..4] OF REAL;
END_VAR
VAR
  lo   : DInt;
  hi   : DInt;
  dim  : Int;
  prod : DInt := 1;
END_VAR
BEGIN
  FOR dim := 1 TO DIM_COUNT_OF(#arr) DO
    lo := LOWER_BOUND(ARR := #arr, DIM := #dim);
    hi := UPPER_BOUND(ARR := #arr, DIM := #dim);
    prod := prod * (hi - lo + 1);
  END_FOR;
  FC_BoundsMulti := prod;
END_FUNCTION
Static-array only. LOWER_BOUND / UPPER_BOUND do not work on ARRAY[*]. For dynamic arrays fall back to CountOfElements (Method 1) or SERIALIZE (Method 4).

Method 4 - SERIALIZE Instruction

SERIALIZE streams the runtime data of a structure or array into a destination ARRAY OF BYTE buffer. The RET_VAL reports the number of bytes actually written (or a negative value if the destination is too small). For arrays of REAL, INT, or other fixed-width primitives, dividing the byte count by the element size recovers the element count. For arrays of STRING or WSTRING, SERIALIZE writes only the populated bytes, giving the true payload length.

SCL example


FUNCTION "FC_SerializeCount" : DInt
VAR_IN_OUT
  arr : ARRAY[*] OF REAL; // requires FW 4.2+
END_VAR
VAR_TEMP
  buf : ARRAY[0..16383] OF BYTE;
  pos : DInt;
END_VAR
BEGIN
  pos := SERIALIZE( SRC_VARIANT := #arr,
                    DEST_ARRAY  := #buf,
                    POS         := 0 );
  IF pos > 0 THEN
    FC_SerializeCount := pos / SIZEOF(REAL);
  ELSE
    FC_SerializeCount := -1; // buffer too small
  END_IF;
END_FUNCTION

Restrictions

  • S7-1200 only on FW 4.2 or newer.
  • Requires the ARRAY[*] (variable-length array) declaration syntax; the static ARRAY[1..N] form on older firmware is rejected by the compiler.
  • Destination buffer must be large enough; otherwise RET_VAL returns a negative value indicating the shortfall.
  • SERIALIZE copies the full runtime data; performance is O(n) over the array size. Avoid calling it inside fast cyclic OB1 if the array is large.

Method 5 - ATTR_DB for Data Block Length

When the question is "how big is this DB?" rather than "how big is this array?", the legacy ATTR_DB instruction returns the maximum byte count of the addressed DB. It is the canonical answer for IEC-style counters walking an entire DB with PEEK / POKE byte-by-byte. ATTR_DB was originally introduced for S7-300/400, ported to S7-1500, and supported on S7-1200 starting with TIA Portal V14 SP1 on FW 4.2.

SCL example


FUNCTION "FC_DBLen" : UDInt
VAR_INPUT
  dbNumber : Int;
END_VAR
VAR_TEMP
  att : STRUCT
    Attrib : Byte;
    Number : Byte;
    Length : UDInt;
  END_STRUCT;
END_VAR
BEGIN
  ATTR_DB(REQ      := TRUE,
          DBNumber := #dbNumber,
          ATTR_DB  := #att);
  FC_DBLen := att.Length;
END_FUNCTION

Interpretation

ATTR_DB returns the byte count of the entire DB including all structures, scalar fields, and arrays. To isolate a single array inside the DB, divide by the element size or use UPPER_BOUND on the array directly. With optimised blocks (default since TIA Portal V14) the byte layout is not contiguous and ATTR_DB returns the logical size, not the physical on-target offset - use UPPER_BOUND on the array symbol rather than arithmetic against ATTR_DB.

Method 6 - Array Bounds Error Technique with GET_ERROR

For older firmware where none of the modern instructions are usable (S7-1200 FW 4.0 / 4.1, TIA Portal V13), one proven technique is to deliberately index past the upper bound, catch the generated range error with GET_ERROR, and decrement the index by one. The technique is robust but intrusive - it relies on triggering a non-fatal error and inspecting it.

SCL example using PEEK


FUNCTION_BLOCK "FB_ScanUpperBound"
VAR_INPUT
  startGuess : DInt := 1;
  maxGuess   : DInt := 32767;
END_VAR
VAR_OUTPUT
  upperIdx : DInt;
  ok       : Bool;
END_VAR
VAR_TEMP
  i    : DInt;
  val  : Int;
  info : Struct
    flags   : Byte;
    opCode  : Word;
  END_STRUCT;
END_VAR
BEGIN
  upperIdx := -1;
  ok := FALSE;
  FOR i := #startGuess TO #maxGuess DO
    // PEEK avoids touching real application data
    val := PEEK(area := 16#84, dbNumber := 100, byteOffset := DINT_TO_INT(i * 2));
    // The above access may generate a range error on the final iteration
    IF NOT ENO THEN
      GET_ERROR(#info);
      upperIdx := i - 1; // last valid index
      ok := TRUE;
      EXIT;
    END_IF;
  END_FOR;
END_FUNCTION_BLOCK
Best practice. Enable array-bounds checking and "Local error handling" inside the FB so that the scan does not bring the CPU into STOP. Set "Local error handling" on the FB / FC properties in TIA Portal, and check ENO after each array read. Without local error handling, the CPU will fall into STOP on the first over-index and the scan cannot recover.

When to use this technique

This is a fallback for legacy hardware that cannot be upgraded. It is not recommended for new code. Performance is O(n) over the maximum possible size, so on an array that might be 32 000 elements the scan takes hundreds of milliseconds and is unsuitable for fast cyclic contexts. Prefer CountOfElements on FW 4.2+ CPUs.

Method 7 - Constants and Parameterised Array Limits

When the array size is known at compile time but used in multiple FB/FCs, declare it once as a global constant and reuse it. This is the lowest-tech answer and the right answer when the size is fixed by the application.

Constant declaration in a global DB


// In a global DB or constant block
CONST
  MY_ARRAY_SIZE : Int := 300;
  MY_ARRAY_LO   : Int := 1;
  MY_ARRAY_HI   : Int := 300;
END_CONST

Array using the constant


DATA_BLOCK "DataDB"
  STRUCT
    MyArray : ARRAY[MY_ARRAY_LO..MY_ARRAY_HI] OF INT;
    // or equivalently
    MyArrayAlt : ARRAY[1..MY_ARRAY_SIZE] OF INT;
  END_STRUCT;
END_DATA_BLOCK

Reusing the constant inside any FB/FC


FUNCTION "FC_AnyUse" : DInt
BEGIN
  FC_AnyUse := MY_ARRAY_SIZE;
END_FUNCTION

Combine constants with UPPER_BOUND so that changing the constant in one place is sufficient. This pattern survives TIA Portal upgrades and is independent of CPU firmware - it is the recommended baseline for any new project.

Comparison Matrix

Method Min S7-1200 FW Min TIA Portal Returns VARIANT OK? Performance Edge cases
CountOfElements 4.2 (1AG40 silicon) V14 Element count Yes Constant time Top-level only by default
SizeOf / divisor 4.0 V13 Byte count Yes Constant time Static-length only; STRING mis-reports
LOWER_BOUND / UPPER_BOUND 4.0 V13 Index values No (typed) Constant time Compile-time type needed
SERIALIZE 4.2 V14 Bytes written Yes O(n) copy Requires buffer; populates STRING
ATTR_DB 4.2 (with V14 SP1) V14 SP1 DB byte count DB only Constant time DB-level, not field-level
Bounds-error scan 4.0 V13 Last valid index No O(n) iteration Triggers error; needs GET_ERROR
Compile-time constant All All Fixed value N/A Compile time Only valid if size is fixed

Decision Tree

  1. Is the array size fixed and known at design time? Use a global constant (Method 7) plus UPPER_BOUND.
  2. Is the CPU firmware 4.2+ on a 1AG40 / 2AG40 / 3AG40 silicon? Use CountOfElements with VARIANT (Method 1).
  3. Is the array passed as a typed parameter? Use LOWER_BOUND and UPPER_BOUND (Method 3).
  4. Do you need the populated byte count for STRING or WSTRING? Use SERIALIZE (Method 4).
  5. Is the question about total DB size rather than a single array? Use ATTR_DB (Method 5).
  6. Are you stuck on FW 4.0 / 4.1 with no upgrade path? Use the bounds-error scan (Method 6) - or, preferably, schedule the upgrade.

Complete Working FB

The following FB consolidates the recommended approaches into a single block. Use the input mode selector to switch between methods without recompiling.


FUNCTION_BLOCK "FB_ArraySize"
VAR_IN_OUT
  vArray : Variant;
END_VAR
VAR_INPUT
  mode : Int; // 1 = CountOfElements, 2 = SizeOf/elem, 3 = SERIALIZE
END_VAR
VAR_OUTPUT
  size : DInt;
  ok   : Bool;
END_VAR
VAR_TEMP
  totalBytes : UDInt;
  elemBytes  : UDInt;
  buf        : ARRAY[0..16383] OF BYTE;
  pos        : DInt;
END_VAR
BEGIN
  size := -1;
  ok   := FALSE;
  CASE #mode OF
    1:
      // Method 1: CountOfElements
      #size := CountOfElements(variant := #vArray);
      #ok   := (#size >= 0);
    2:
      // Method 2: SizeOf / element size
      #totalBytes := SizeOf(variant := #vArray);
      #elemBytes  := 2; // adjust per element type, e.g. SIZEOF(INT)
      IF #elemBytes > 0 THEN
        #size := DINT_TO_DINT(UDINT_TO_DINT(#totalBytes) / UDINT_TO_DINT(#elemBytes));
        #ok   := TRUE;
      END_IF;
    3:
      // Method 4: SERIALIZE
      #pos := SERIALIZE(SRC_VARIANT := #vArray,
                        DEST_ARRAY  := #buf,
                        POS         := 0);
      IF #pos > 0 THEN
        #size := #pos / 2; // assume 2-byte element; adjust per element type
        #ok   := TRUE;
      END_IF;
  ELSE
    #size := -1;
    #ok   := FALSE;
  END_CASE;
END_FUNCTION_BLOCK

Verification Procedure

Before deploying any of the above techniques to production, verify with the following checklist:

  1. Confirm the project uses a TIA Portal version at least as new as the firmware's release. Mismatched versions compile in TIA Portal but fail to download.
  2. Add a watch table on the array's declared length, the result DInt, and the running CPU firmware version. Read online to confirm FW.
  3. Set a breakpoint inside the SCL block that returns the size. Run the program in single-step mode and confirm the value matches the array declaration.
  4. If using PLCSIM, use V15.1 or later - earlier PLCSIM does not simulate ARRAY[*] and silently returns zero.
  5. If using the bounds-error technique, deliberately trigger an over-index and confirm the CPU stays in RUN with the error captured in the diagnostic buffer (CPU > Online & Diagnostics > Diagnostic buffer).
  6. Reset the CPU to factory settings before final acceptance to confirm no stale array contents bias the test.
  7. Check the diagnostic buffer for OB1 execution errors. A pattern of "Range length error" events indicates the bounds-error technique is active and may need tuning.
  8. For S7-1200 FW 4.2 on 1AG40 silicon: confirm the order code ends in 1AG40, 2AG40, or 3AG40. Older silicon (e.g. 1AY10) does not support ARRAY[*] regardless of firmware.

Troubleshooting Matrix

Symptom Likely Cause Fix
CountOfElements instruction greyed out CPU FW older than 4.2 or wrong CPU variant Upgrade to FW 4.2; switch to SizeOf / UPPER_BOUND
Compile error "VARIANT cannot be passed by reference" Array wired to a typed IN_OUT Change the parameter type to VARIANT
SERIALIZE returns -1 Destination buffer too small Increase ARRAY OF BYTE size or check RET_VAL for the shortfall
ATTR_DB returns invalid length DB number does not exist or FW too old Verify DB is downloaded; upgrade FW to 4.2 + TIA V14 SP1
Bounds-error scan puts CPU into STOP Local error handling not enabled FB properties > "Local error handling" = enabled; check ENO after each access
PLCSIM returns 0 for array length PLCSIM too old for ARRAY[*] Use PLCSIM V15.1 or later
SizeOf returns 0 for an empty ARRAY Compiler optimised the array away Mark the block as non-optimisable or add a runtime touch
UPPER_BOUND returns incorrect dimension Dim parameter set to wrong dimension number Iterate DIM_COUNT_OF from 1
Online value differs from offline Different firmware between offline and online project Re-download project with matched firmware
CountOfElements returns -1 VARIANT pointer invalid (source block deleted) Recompile and re-download; refresh VARIANT pointer

Edge Cases and Field-Notes

Multi-dimensional arrays. CountOfElements returns the product of every dimension's length when the input is a multi-dimensional array of a single type. For mixed structures (arrays of structs of varying widths), use SERIALIZE with a custom parser or maintain a constant for each sub-dimension.

VARIANT lifetime. The VARIANT pointer is invalidated when the source block is deleted or its number changes. Always re-fetch the variant after any structural recompile. If the source DB is renamed the variant assignment in OB1 must be rewired.

Optimised block access. Optimised blocks (default since TIA Portal V14) remove the absolute addresses that PEEK / POKE rely on. The bounds-error technique requires unoptimised access for the array or a different probe function. To disable optimisation: right-click the DB > Properties > Attributes > "Optimised block access" > uncheck.

STRING length within arrays. SizeOf reports the declared length (e.g. 254 bytes for STRING[254]) even when the actual content is shorter. Use LEN on each element for true content length. SERIALIZE is the only instruction that respects the runtime payload for arrays of STRING.

Cross-block reuse. When an array is passed across FB/FC boundaries in a multi-instance scenario, prefer UPPER_BOUND inside the FB to recover the dimension count. This avoids the runtime cost of CountOfElements on every call and works on every firmware.

BOOL packing. TIA Portal packs BOOL arrays into the lower bits of bytes. An ARRAY[0..15] OF BOOL consumes 2 bytes, not 16. Divide SizeOf by 1 and use UPPER_BOUND for the index count; do not multiply.

Siemens engineering tip. When debugging an unknown third-party block that consumes an array, place the array inside a wrapper DB with a sentinel at index 0. Write a known pattern, then read back to confirm the wrapper is wired correctly before chasing the size call.

Performance Comparison

Method Typical execution time on S7-1200 FW 4.4 Notes
CountOfElements < 1 microsecond Pointer chase + table lookup
SizeOf < 1 microsecond Compile-time constant for static arrays
LOWER_BOUND / UPPER_BOUND < 1 microsecond Compile-time constant for static arrays
SERIALIZE ~1 microsecond per element + overhead Memory copy
ATTR_DB 10-50 microseconds System call
Bounds-error scan 10 microseconds per iteration O(n) over max possible size

Numbers are empirical ballparks; exact timing depends on CPU variant, scan cycle load, and memory layout. For arrays under 1000 elements, the absolute difference is negligible and the choice should be driven by clarity and firmware availability.

Documentation Pointers

Engineers should consult the following Siemens-authored documents for definitive behaviour:

  • SIMATIC S7-1200 Programmable Controller - System Manual (latest edition; entry ID 109766459 on the Siemens Industry Online Support portal).
  • SIMATIC S7 SCL - Programming Manual (latest edition).
  • TIA Portal Help (F1 in the TIA Portal editor) for instruction reference under "Basic instructions > Extended instructions" and "Basic instructions > Program control operations".
  • Siemens Industry Online Support portal at support.industry.siemens.com for the S7-1200 firmware release notes - search "S7-1200 firmware" for the canonical FW matrix.

FAQ

What is the simplest way to get the size of an ARRAY in SCL on S7-1200?

Declare a global CONST for the size and reference it from every block that needs the length. If you need a runtime answer, use UPPER_BOUND(arr) - LOWER_BOUND(arr) + 1, which works on every S7-1200 firmware from 4.0 onward with TIA Portal V13.

Is CountOfElements available on S7-1200 FW 4.1?

No. CountOfElements requires FW 4.2 on the 1AG40 / 2AG40 / 3AG40 silicon. On older firmware the instruction is greyed out in TIA Portal or rejected on download. Use SizeOf divided by the element byte length instead.

Why does PLCSIM return 0 for an ARRAY[*] length?

PLCSIM versions older than V15.1 do not simulate the variable-length array syntax. Upgrade PLCSIM to V15.1 or later, or test the same code on the physical CPU which always honours ARRAY[*] from FW 4.2.

Can SizeOf give me the populated length of a STRING array?

No. SizeOf returns the declared byte length of each element (for example 254 bytes for STRING[254]) even when the actual content is shorter. To read the populated length use the LEN instruction on each element, or use SERIALIZE to inspect the actual bytes written.

Does ATTR_DB tell me the array size or only the total DB size?

ATTR_DB returns the byte count of the entire DB, including all structures, scalar fields, and arrays. To isolate a single array inside the DB, divide the byte count by the element size or use UPPER_BOUND on the array symbol directly. With optimised blocks the byte layout is logical, not physical, so prefer UPPER_BOUND when possible.

How do I keep my CPU from going into STOP when probing array bounds?

Enable "Local error handling" on the FB or FC properties in TIA Portal, then check ENO after each array access. With local error handling enabled the over-index produces a non-fatal error that GET_ERROR can inspect; without it the CPU falls into STOP on the first violation.

Back to blog