Developer Portal Community

    cancel
    Showing results for 
    Search instead for 
    Did you mean: 

    Correct usage of string function OpconStrCat (CONCAT)

    English 🠦 Deutsch
     

    The Nexeed string function OpconStrCat() for string concatenation must not be called with SIZEOF(). A call with SIZEOF()-1 is possible.

    Incorrect use of OpconStrCat

    • OpconStrCat(pDestStr   := ADR(_tempString1), DestStrLen   := SIZEOF(_tempString1),
                  pAppendStr := ADR(_tempString2), AppendStrLen := SIZEOF(_tempString2));

     

    Correct use of OpconStrCat

    • OpconStrCat(pDestStr   := ADR(_tempString1), DestStrLen   := SIZEOF(_tempString1)-1,
                  pAppendStr := ADR(_tempString2), AppendStrLen := SIZEOF(_tempString2)-1);
    • OpconStrCat(pDestStr   := ADR(_tempString1), DestStrLen   := TEMP_STRING_1_LEN,
                  pAppendStr := ADR(_tempString2), AppendStrLen := TEMP_STRING_2_LEN);
    • As all other OpconStr functions expect the actual string length as input parameter, OpconStrCat can also be used with it. OpconStrCat calls OpconStrLen again internally, which is why the use of OpconStrLen is optional.
      OpconStrCat(pDestStr     := ADR(_tempString1),
      DestStrLen := OpconStrLen(ADR(_tempString1), SIZEOF(_tempString1)-1),
      pAppendStr := ADR(_tempString2),
      AppendStrLen := OpconStrLen(ADR(_tempString2), SIZEOF(_tempString2)-1));

     

    Explanation

    When the transferred maximum string length is reached, these two functions write a 0 (null) as a string terminator in the subsequent memory area. If SIZEOF() is used without -1, this memory area lies outside the target string variable, e.g. on another variable or on the call stack. This leads to unnoticed misbehavior, unexpected variable values, page fault exceptions or TwinCAT changes directly from the RUN state to CONFIG without an error message.

    The following online display "Value of the expression cannot be retrieved" can be an indication of such faulty memory operations:

    SteffenR_1-1723638545525.png

    String size and string length have a different meaning, as a string always requires a terminator null in addition to the characters:

    VAR
      _tempString1 : STRING(TEMP_STRING_1_LEN);
      _tempString2 : STRING;
    END_VAR
    VAR CONSTANT
      TEMP_STRING_1_LEN : UDINT := 255;
    END_VAR


    _size := SIZEOF(_tempString1); // = 256
    _length := OpconStrLen(ADR(_tempString1), SIZEOF(_tempString1)); // = 0..255 (number of characters used)

    _size := SIZEOF(_tempString2); // = 81
    _length := OpconStrLen(ADR(_tempString2), SIZEOF(_tempString2)); // = 0..80 (number of characters used)

     

    Possible use of OpconStrCat with disadvantages

    • If the string length is changed in the declaration of _tempString1 or _tempString2, you must also remember to change these code positions. According to the coding directives, constants should be used.
      OpconStrCat(pDestStr   := ADR(_tempString1), DestStrLen   := 255,
      pAppendStr := ADR(_tempString2), AppendStrLen := 80);
    • According to the coding guidelines, only OpconStr functions should be used. Like all other string functions, LEN is limited to 255 characters. Contrary to the coding guidelines, you could then also use CONCAT instead of OpconStrCat.
      OpconStrCat(pDestStr   := ADR(_tempString1), DestStrLen   := INT_TO_UDINT(LEN(_tempString1)),
      pAppendStr := ADR(_tempString2), AppendStrLen := INT_TO_UDINT(LEN(_tempString2)));

     

    Hints about other string functions

    • OpconStrLen
      This function works with both SIZEOF() and SIZEOF()-1, as it only determines the actual string length. If the terminator null is missing, it is safer to use SIZEOF()-1 or a constant for the string length.
      • OpconStrLen(pString := ADR(_tempString1), MaxLen := SIZEOF(_tempString1)-1);
      • OpconStrLen(pString := ADR(_tempString1), MaxLen := TEMP_STRING_1_LEN);
    • OpconStrTrim
      When called with SIZEOF(), it is also written outside the memory area of the string variable. This function expects the actual string length as a input parameter, as otherwise an invalid string may be returned as the result. In contrast to OpconStrCat, OpconStrLen is not called internally. Therefore, only the following usage is correct:
      • OpconStrTrim(pString := ADR(_tempString1),
        StrLen := OpconStrLen(ADR(_tempString1), SIZEOF(_tempString1)));
    • OpconStrCmp (String Compare), OpconStrDelete, OpconStrFind, OpconStrToLower, OpconStrToUpper
      These functions expect the actual string length as a input parameter, otherwise an incorrect result will be returned. OpconStrLen must therefore be used to determine the string length.
      • OpconStrCmp(pString1   := ADR(_tempString1),
        String1Len := OpconStrLen(ADR(_tempString1), SIZEOF(_tempString1)),
        pString2 := ADR(_tempString2),
        String2Len := OpconStrLen(ADR(_tempString2), SIZEOF(_tempString2)));
     
     
    Deutsch 🠦 English
     

    Die Nexeed String-Funktion OpconStrCat() für String-Verknüpfung darf nicht mit SIZEOF() aufgerufen werden. Ein Aufruf mit SIZEOF()-1 ist möglich.

    Fehlerhafte Anwendung von OpconStrCat

    • OpconStrCat(pDestStr   := ADR(_tempString1), DestStrLen   := SIZEOF(_tempString1),
                  pAppendStr := ADR(_tempString2), AppendStrLen := SIZEOF(_tempString2));

     

    Korrekte Anwendung von OpconStrCat

    • OpconStrCat(pDestStr   := ADR(_tempString1), DestStrLen   := SIZEOF(_tempString1)-1,
                  pAppendStr := ADR(_tempString2), AppendStrLen := SIZEOF(_tempString2)-1);
    • OpconStrCat(pDestStr   := ADR(_tempString1), DestStrLen   := TEMP_STRING_1_LEN,
                  pAppendStr := ADR(_tempString2), AppendStrLen := TEMP_STRING_2_LEN);
    • Da alle anderen OpconStr-Funktionen die tatsächliche Stringlänge als Übergabe-Parameter erwarten, kann man auch OpconStrCat damit verwenden. OpconStrCat ruft intern nochmals OpconStrLen auf, weshalb die Verwendung von OpconStrLen optional ist.
      OpconStrCat(pDestStr     := ADR(_tempString1),
      DestStrLen := OpconStrLen(ADR(_tempString1), SIZEOF(_tempString1)-1),
      pAppendStr := ADR(_tempString2),
      AppendStrLen := OpconStrLen(ADR(_tempString2), SIZEOF(_tempString2)-1));

     

    Erklärung

    Bei Erreichen der übergebenen maximalen Stringlänge schreiben diese zwei Funktionen in den darauffolgenden Speicherbereich eine 0 (Null) als String-Terminator. Bei Verwendung von SIZEOF() ohne -1 liegt dieser Speicherbereich außerhalb der Ziel-String-Variable, z.B. auf einer anderen Variable oder auf dem Call Stack. Dies führt zu unbemerktem Fehlverhalten, unerwarteten Variablenwerten, Page Fault Exceptions (Ausnahmefehler) oder TwinCAT wechselt direkt vom Zustand RUN in CONFIG ohne Fehlermeldung.

    Folgende Online-Darstellung "Value of the expression cannot be retrieved" kann ein Hinweis auf derartige fehlerhafte Speicheroperationen sein:

    SteffenR_1-1723638545525.png

    String-Größe und String-Länge haben eine unterschiedliche Bedeutung, da ein String immer zusätzlich zu den String-Zeichen eine Terminator-Null benötigt:

    VAR
      _tempString1 : STRING(TEMP_STRING_1_LEN);
      _tempString2 : STRING;
    END_VAR
    VAR CONSTANT
      TEMP_STRING_1_LEN : UDINT := 255;
    END_VAR


    _size := SIZEOF(_tempString1); // = 256
    _length := OpconStrLen(ADR(_tempString1), SIZEOF(_tempString1)); // = 0..255 (Anzahl der verwendeten Zeichen)

    _size := SIZEOF(_tempString2); // = 81
    _length := OpconStrLen(ADR(_tempString2), SIZEOF(_tempString2)); // = 0..80 (Anzahl der verwendeten Zeichen)

     

    Funktionierende Anwendungen von OpconStrCat mit Nachteilen

    • Wenn die Stringlänge in der Deklaration von _tempString1 bzw. _tempString2 geändert wird, muss man daran denken, diese Code-Stellen auch zu ändern. Laut Codierrichtlinien sollen Konstanten verwendet werden.
      OpconStrCat(pDestStr   := ADR(_tempString1), DestStrLen   := 255,
      pAppendStr := ADR(_tempString2), AppendStrLen := 80);
    • Laut Codierrichtlinien sollen nur OpconStr-Funktionen verwendet werden. LEN ist wie alle anderen String-Funktionen auf 255 Zeichen begrenzt. Man könnte dann entgegen der Codierrichtlinien auch CONCAT anstatt OpconStrCat verwenden.
      OpconStrCat(pDestStr   := ADR(_tempString1), DestStrLen   := INT_TO_UDINT(LEN(_tempString1)),
      pAppendStr := ADR(_tempString2), AppendStrLen := INT_TO_UDINT(LEN(_tempString2)));

     

    Hinweise zu weiteren String-Funktionen

    • OpconStrLen
      Diese Funktion funktioniert sowohl mit SIZEOF() als auch mit SIZEOF()-1, da sie nur die tatsächliche Stringlänge ermittelt. Falls die Terminator-Null fehlt, ist die Verwendung mit SIZEOF()-1 oder Konstante für die Stringlänge sicherer.
      • OpconStrLen(pString := ADR(_tempString1), MaxLen := SIZEOF(_tempString1)-1);
      • OpconStrLen(pString := ADR(_tempString1), MaxLen := TEMP_STRING_1_LEN);
    • OpconStrTrim
      Bei Aufruf mit SIZEOF() wird ebenfalls außerhalb des Speicherbereichs der String-Variable geschrieben. Diese Funktion erwartet die tatsächliche Stringlänge als Übergabeparameter, da ansonsten ein ungültiger String als Ergebnis ausgegeben werden kann. Im Gegensatz zu OpconStrCat erfolgt kein interner Aufruf von OpconStrLen. Deshalb ist nur folgende Anwendung korrekt:
      • OpconStrTrim(pString := ADR(_tempString1),
        StrLen := OpconStrLen(ADR(_tempString1), SIZEOF(_tempString1)));
    • OpconStrCmp (String Compare), OpconStrDelete, OpconStrFind, OpconStrToLower, OpconStrToUpper
      Diese Funktionen erwarten die tatsächliche Stringlänge als Übergabeparameter, da ansonsten ein falsches Ergebnis geliefert wird. Es muss deshalb OpconStrLen zur Ermittlung der Stringlänge verwendet werden.
      • OpconStrCmp(pString1   := ADR(_tempString1),
        String1Len := OpconStrLen(ADR(_tempString1), SIZEOF(_tempString1)),
        pString2 := ADR(_tempString2),
        String2Len := OpconStrLen(ADR(_tempString2), SIZEOF(_tempString2)));
    Version history
    Last update:
    ‎08-15-2024 02:59 PM
    Updated by:
    Contributors
    Icon--AD-black-48x48Icon--address-consumer-data-black-48x48Icon--appointment-black-48x48Icon--back-left-black-48x48Icon--calendar-black-48x48Icon--center-alignedIcon--Checkbox-checkIcon--clock-black-48x48Icon--close-black-48x48Icon--compare-black-48x48Icon--confirmation-black-48x48Icon--dealer-details-black-48x48Icon--delete-black-48x48Icon--delivery-black-48x48Icon--down-black-48x48Icon--download-black-48x48Ic-OverlayAlertIcon--externallink-black-48x48Icon-Filledforward-right_adjustedIcon--grid-view-black-48x48IC_gd_Check-Circle170821_Icons_Community170823_Bosch_Icons170823_Bosch_Icons170821_Icons_CommunityIC-logout170821_Icons_Community170825_Bosch_Icons170821_Icons_CommunityIC-shopping-cart2170821_Icons_CommunityIC-upIC_UserIcon--imageIcon--info-i-black-48x48Icon--left-alignedIcon--Less-minimize-black-48x48Icon-FilledIcon--List-Check-grennIcon--List-Check-blackIcon--List-Cross-blackIcon--list-view-mobile-black-48x48Icon--list-view-black-48x48Icon--More-Maximize-black-48x48Icon--my-product-black-48x48Icon--newsletter-black-48x48Icon--payment-black-48x48Icon--print-black-48x48Icon--promotion-black-48x48Icon--registration-black-48x48Icon--Reset-black-48x48Icon--right-alignedshare-circle1Icon--share-black-48x48Icon--shopping-bag-black-48x48Icon-shopping-cartIcon--start-play-black-48x48Icon--store-locator-black-48x48Ic-OverlayAlertIcon--summary-black-48x48tumblrIcon-FilledvineIc-OverlayAlertwhishlist