Dynamic memory

Topics:

  1. Perform dynamic memory allocations carefully [++]
Dynamic memory 1:

PLC_Library Tc3_DynamicMemory

The use of dynamic memory can be simplified with the help of the PLC library Tc3_DynamicMemory.

Perform dynamic memory allocations carefully

Dynamic memory allocations using __NEW() should be done with care. The reason is that they change the memory requirement and array limits at runtime, for example. These changes should always be taken into account, in order to prevent unauthorized memory access resulting in a program crash.

With dynamic memory allocation it is particularly important to observe which memory is allocated and how often. Since dynamic memory allocations may affect the runtime and perhaps the memory, they should only be carried out once and at certain times, such as during startup or after a system retrofit.

Also note that dynamically allocated memory must subsequently be released again using __DELETE().

Negative sample:

FUNCTION_BLOCK FB_Sample_neg
VAR
    pDynamicLRealArray : POINTER TO LREAL;  // Pointer to dynamic array
    nArrayCounter      : INT;               // Counter variable
    bInit              : BOOL;              // Initialize array only once
END_VAR
(* NON COMPLIANT:
1: If the amount of new LREALs is constant (in this case 10 elements), use a static ARRAY [0..10] OF LREAL.
2: If the amount of new LREALs is dynamic, do not use magic numbers. If the amount of elements (10) is changed here, there would be no way to ensure that this number is also changed in any other code parts.
3: If an array is created dynamically and assigned to a pointer, do not use this original pointer to work on the array. The start address of the array would be overwritten. *)
IF NOT bInit THEN
    pDynamicLRealArray := __NEW(LREAL, 10);                   // NON COMPLIANT: see above (1, 2)
    FillDynamicArray();
    bInit              := TRUE; 
END_IF

Method FillDynamicArray:

FOR nArrayCounter := 1 TO 10 DO                                // NON COMPLIANT: see above (2)
    pDynamicLRealArray^ := 1.25;
    pDynamicLRealArray  := pDynamicLRealArray + SIZEOF(LREAL); // NON COMPLIANT: see above (3)
END_FOR

Positive sample:

FUNCTION_BLOCK FB_Sample_pos
VAR
    pDynamicLRealArray  : POINTER TO LREAL; // Pointer to dynamic array
    nDynamicArrayLen    : INT;              // Dynamic array length
    nArrayCounter       : INT;              // Counter variable
    pDynamicLRealTemp   : POINTER TO LREAL; // Temp. pointer to dynamic array
    bInit               : BOOL;             // Initialize array only once
END_VAR
IF NOT bInit THEN
    nDynamicArrayLen    := F_CalculateBufferLen();   
    pDynamicLRealArray  := __NEW(LREAL, nDynamicArrayLen);     // COMPLIANT
    FillDynamicArray();
    bInit               := TRUE;
END_IF

Method FillDynamicArray:

// Do not work on the array with the pointer that points to the start address of the dynamic array. Use a temporary pointer for working on the array to keep the start address of the array.
pDynamicLRealTemp := pDynamicLRealArray;

// The following FOR-statement is COMPLIANT if nDynamicArrayLen is the length of array in any case.
FOR nArrayCounter := 1 TO nDynamicArrayLen DO 
    pDynamicLRealTemp^ := 1.25;
    pDynamicLRealTemp  := pDynamicLRealTemp + SIZEOF(LREAL);   // COMPLIANT: Temporary pointer used to work on the array
END_FOR

Alternative method FillDynamicArray:

// The following FOR-statement is COMPLIANT if nDynamicArrayLen is the length of array in any case.
FOR nArrayCounter := 1 TO nDynamicArrayLen DO 
    pDynamicLRealArray[nArrayCounter-1] := 1.25;               // COMPLIANT: Pointer is not changed
END_FOR