Level 1, lesson 20

# 1.20 Terminating strings

We promised you we would get back on this issue. Like we said, 4tH terminates strings for you. But if you do not take the terminator into account when moving strings, you have to terminate them yourselves. Let's take a look at this program:

         32 string one                   \ define string one
         32 string two                   \ define string to
         " Hans" one copy                \ initialize string one
         count                           ( address1 length1)
         two                             ( address1 length1 address2)
         swap                            ( address1 address2 length1)
         cmove                           \ copy string1 to string2

Note that we didn't copy the terminator! If we add the following line to this program it might work. Or not!

         two count type cr

If there was accidentally a terminator in string "two" at the right place it might work. If there wasn't, it won't! Let's make sure there was no terminator at the right place by initializing string "two" too:

         32 string one                   \ define string one
         32 string two                   \ define string to
         " Hans" one copy                \ initialize string one
         count                           ( address1 length1)
         " Bezemer" two copy             ( address1 length1 address2)
         swap                            ( address1 address2 length1)
         cmove                           \ copy string1 to string2
         two count type cr               \ print string two

This will most certainly print garbage. How can we get things right? By terminating string "two". The terminator must be stored in the fifth character (since "Hans" is four characters long). Since we count from zero we write:

         32 string one                   \ define string one
         32 string two                   \ define string to
         " Hans" one copy                \ initialize string one
         count                           ( address1 length1)
         " Bezemer" two copy             ( address1 length1 address2)
         swap                            ( address1 address2 length1)
         cmove                           \ copy string1 to string2
         0                               \ throw terminator on stack
         two                             \ throw address on stack
         4 chars +                       \ reference 5th character
         c!                              \ store terminator
         two count type cr               \ print string two

Of course this "hardcoding" makes our program very inflexible. E.g. if we change "Hans" to "Jim" or "James" it won't work anymore. So we make an improved version of this program. We will only comment the stack- effects. String "one" is represented by "a1", string "two" by "a2", the length of string "one" by "n1", the length of string "two" by "n2":

         32 string one                   ( --)
         32 string two                   ( --)
         " Hans" one copy                ( a1)
         count                           ( a1 n1)
         dup                             ( a1 n1 n1)
         rot                             ( n1 n1 a1)
         " Bezemer" two copy             ( n1 n1 a1 a2)
         rot                             ( n1 a1 a2 n1)
         cmove                           ( n1)
         0                               ( n1 0)
         two                             ( n1 0 a2)
         rot                             ( 0 a2 n1)
         chars +                         ( 0 a2+n1)
         c!                              ( --)
         two count                       ( a2 n2)
         type cr                         ( --)

WOW!! Yes, you learned your lesson well! Although it is possible to terminate a string afterwards, it is usually easier to take the terminator into account when you move strings!

Note that you throw the ASCII-value of the terminator onto the stack. Using the expression:

         char 0

Will throw the ASCII-value of the character "0" on the stack, which is something completely different!

Finally, you may wonder how we knew where to store the terminator. If you come to think about it, it is very easy. Take this program:

         32 string one                   \ define string one
         " Hans" one copy                \ initialize string one
         count                           \ get its length
         chars + c@ . cr                 \ print ASCII at pos 'length'

Whatever you put in string "one", it will always print "0". Think about it: what is stored there? The length of string "one" is four charac- ters. So we look what's stored at position 4. At the zeroth position we find the character "H". At the first position "a". At the second "n". At the third "s". And finally, at the fourth the terminator, which is ASCII zero.