Pointer and References in a Nutshell

Pointers and references are both powerful features in TwinCAT 3 that allow for efficient memory management and manipulation of data. However, they have distinct differences that are important to understand when designing and implementing your code. This blog article will give you a brief overview of the definition, the advantages and disadvantages of pointers and references, and which application they are best suited for. The following examples, made for TwinCAT 3, should clarify the choice.

Why should we use it?

A pointer or reference is stored in as many bytes as it takes to hold an address on the computer. This often makes them much smaller than the things they point to. We take advantage of this small size when storing data and when passing parameters to functions. It is much faster and more memory-efficient to copy a pointer or a reference than to copy many of the things they are likely to point to.

Naked facts about pointers

  • A pointer is a variable that holds the memory location of another variable.
  • The pointer variable returns the value located at the address stored in the pointer variable which is preceded by the pointer sign ‚^‘.
  • Operators in TwinCAT: ^, ADR()
  • The pointer variable can refer to NULL.
  • An uninitialized pointer can be created.
  • The pointer variable can be initialized at any point of time in the program.
  • The pointer variable can be reinitialized as many times as needed.
  • The result of the difference between two pointers is of type DWORD, even on 64-bit platforms if the pointers are 64-bit pointers.
  • UNCTION CheckPointer

Naked facts about references

  • The reference variable is used to refer to the variable which is assigned to that reference variable.
  • The reference variable returns the address of the variable.
  • Operators in TwinCAT: REF= or :=
noun-information-6735183-FFFFFF_edited
An exception to this is when an input is a “REFERENCE TO” and the input is transferred within the call. In this case, the normal allocation operator := is used instead of the allocation operator REF=.
  • The reference variable can never refer to NULL.
  • An uninitialized reference can never be created.
  • The reference variable can only be initialized at the time of its creation.
  • The reference variable can never be reinitialized again in the program.
  • Check functions: use operator __ISVALIDREF

How to use pointers in TwinCAT 3

In TwinCAT3, pointers are declared using the „POINTER TO“ keyword, followed by the data type of the variable that the pointer points to. For example, to create a pointer to an integer variable, you would use the following syntax:

VAR
  myPointer : POINTER TO INT; 
END_VAR

To assign the memory address of a variable to a pointer, use the „ADR“ operator. For example, to assign the memory address of the variable „myVar“ to the pointer „myPointer“, you would use the following code:

myPointer := ADR(myVar);

To access the value of the variable that a pointer points to, you use the „^“ operator. For example, to access the value of the variable „myVar“ using the pointer „myPointer“, you would use the following code:

myPointer^ := 5;

How to use references in TwinCAT 3

References, on the other hand, are declared using the „REFERENCE TO“ keyword, followed by the data type of the variable that the reference refers to. For example, to create a reference to an integer variable, you would use the following syntax:

VAR
  myReference : REFERENCE TO INT;
END_VAR

For assignments in the code body (not calls) use the “REF=” assignment. For example, to assign the variable „myVar“ to the reference „myReference“, you would use the following code:

myReference REF= myVar;

To access the value of the variable that a reference refers to, you use the variable name without an additional operator. For example, to access the value of the variable „myVar“ using the reference „myReference“, you would use the following code:

myReference := 5;

Use cases and code examples

Demonstration of the type independence of pointers compared to references

VAR
  x : INT;
  y : INT;
  z : REAL;
  int_ptr : POINTER TO INT;
  int_ref : REFERENCE TO INT;
  real_ref : REFERENCE TO REAL;
  ptrValue : INT;
  intRefValue : INT;
  realRefValue : REAL;
END_VAR
x := 5;
y := 10;
z := 3.14;

(* Pointers *)
int_ptr := ADR(x); // Pointer to an INT
int_ptr^ := y; // Dereference the pointer and change the value of x to y
ptrValue := int_ptr^; // Output: x = 10

int_ptr := ADR(z); // Assign a REAL address to an INT pointer
ptrValue := int_ptr^; // Output: z = undefined
(* This is allowed, but could lead to undefined behavior *)

(* References *)
int_ref REF= x; // Reference to an INT
int_ref REF= y; // Change the value of x to y
intRefValue := int_ref; // Output: x = 10

real_ref REF= z; // Reference to a REAL
real_ref := 2.718;
realRefValue := real_ref; // Output: z = 2.718

Pointers used for dynamic arrays

VAR
  dynamicArray : POINTER TO INT;
  elementOutput : INT;
  i : INT;
END_VAR

(* Allocate memory for the dynamic array *)
dynamicArray := __NEW(INT, 10);

(* Initialize the elements of the dynamic array *)
FOR i := 0 TO 9 DO
dynamicArray[i] := i * i;
END_FOR;

(* Access and show the elements of the dynamic array *)
FOR i := 0 TO 9 DO
elementOutput := dynamicArray[i];
END_FOR;

(* Deallocate memory for the dynamic array *)
__DELETE(dynamicArray);
noun-information-6735183-FFFFFF_edited
It is important to make sure to de-allocate memory for dynamic arrays after using them, to avoid memory leaks.

Pointer arithmetic example

VAR
  myArray : ARRAY[1..6] OF INT := [1, 2, 3, 4, 5, 6];
  PtrStart : POINTER TO INT := ADR(myArray);
  PtrItem : POINTER TO INT;
  elementOutput : INT;
  bMove : BOOL;
  i : INT := 1;
END_VAR

IF bMove THEN // start trigger to move pointer to next array element
  ptrItem := ptrStart + (i - 1) * SIZEOF(INT); // increment pointer address by 1 with address size of array element (INT)
  elementOutput := ptrItem^; // output of the vaule from the pointed element
  i := i + 1; // increment by 1
  IF i = 7 THEN // reset index value at the end of last array element to avoid access to undefinded storage area
    i := 1;
  END_IF
END_IF

bMove := FALSE; // one cycle done
noun-information-6735183-FFFFFF_edited
Note that incrementing a pointer that contains a null pointer value is strictly undefined behavior. Normally, you should only use increment/decrement when pointing to elements of an array.

Reference use case: Pass input signal by references via FB_INIT into a class

At the outside of the simple senor class the hardware input is assigned to the reference “di_Sensor”:

{attribute 'qualified_only'}
VAR_GLOBAL
  {region "Sensors"}
  partOnLocationSensor : SimpleSensor(di_Sensor := GVL_PcInterface.ios.input[BinaryHwInputs._EBOX_SRM_K201_1_I1],
  stateOccupied := TRUE );
  {endregion}
END_VAR

In the FB_INIT of the simple sensor the reference is referenced to the sensor input variable of the class:

METHOD FB_INIT
VAR_INPUT
  bInitRetains: BOOL;
  bInCopyCode: BOOL;
  ///mask for the sensor state occupied
  stateOccupied: BOOL;
  ///input of the sensor
  di_Sensor: REFERENCE TO BOOL;
END_VAR

THIS^.stateOccupied := stateOccupied ;
THIS^.di_Sensor REF= di_Sensor ;
noun-information-6735183-FFFFFF_edited
A reference always needs to be initialized with a variable! The variable must exist before the reference will be initialized with this variable!

Inside of the class, you can use the input value of the sensor signal like every other common data type without address operator or de referencing:

FUNCTION_BLOCK SimpleSensor IMPLEMENTS ISensor
VAR
  ///mask for the sensor state occupied
  stateOccupied: BOOL;
  ///input of the sensor
  di_Sensor: REFERENCE TO BOOL;
  //just for commissioning if sensor does not exist
  simulate :BOOL := FALSE;
END_VAR

METHOD isFree : BOOL
isFree := ((THIS^.di_Sensor = (NOT THIS^.stateOccupied)) OR THIS^.simulate);

So far, so good! But when do I use what?

Advantages of pointers

  1. Null-ability: Pointers can be set to the null value, whereas references cannot. This allows for more flexibility in situations where a variable may or may not be present.
  2. Aliasing: Pointers can point to different variables at different times, whereas references are bound to a specific variable and cannot be reassigned. This can be useful when working with complex data structures and performing operations on multiple variables at the same time.
  3. Dynamic memory allocation: Pointers can be used to dynamically allocate memory on the heap, while references cannot. This allows for more dynamic and flexible use of memory in situations where the amount of memory required may not be known at compile time.
  4. Pointers are more efficient for passing large data structures to a function by reference.
  5. Pointers can also be used to create linked data structures such as linked lists and trees. This is not possible with references.

Advantages of references

  1. Easier to use: The reference does not have to be de-referenced (with ^) to access the contents of the referenced object.
  2. Cleaner syntax for transferring values: If an input is a “REFERENCE TO”, there is no need to write ADR(value) at a (refInput := value).
  3. Type safety: In contrast to pointers, for references the compiler checks whether the base type is the same when assigning two references. (Please note that for pointers this check can be performed using the compiler extension TE1200 Static Analysis. See rule SA0019.)

Conclusion

The biggest difference between pointers and references is that pointers are way more powerful than references but less safe to use. With pointers you can do a lot more things, like working with dynamic storage and arithmetic calculations. Also, pointers are not type-related, which is useful when you are calculating with stings. However, it is important to note that pointers are generally considered more error-prone than references, as they can be de-referenced improperly, leading to undefined behavior or memory access violations. Also, references are safer to use because they cannot be null, and their type is known at compile time. In general, it is recommended that references be used in most cases. Pointers should be reserved for special cases where their advantages outweigh their disadvantages.

 

For more insights on this topic, check out our in-depth video on pointers and references on YouTube here.

@Ninjamonkeystutorials