Arrays, Strings and References

A string, such as "Hello World" is obviously a sequence of characters. This is literally true in C and C++. An array is a sequence of data variables. Each variable in the sequence is given an index starting from 0. What's interesting about arrays is that the index can be a mathematical expression whose value is unknown until the moment before the array is accessed. This is a big advantage over simple variables, whose names are hard-coded into the program. A variable i is never the variable j. What you write is all you get.

Here is a simple example of an array of three characters.

    {
         char ca[3] ;    // three characters variables
         ca[0] = 't' ;
         ca[1] = 'o' ;
         ca[2] = 'p' ;  
         cout << ca[0] << ca[1] << ca[2] ;
         cout << endl ;  // newline, so it looks nice.
    }
Like any variable, the array must be declared. The compile wants to know the name of the variable, it's underlying type, and, in the case of an array, how many in the sequence it is to create. Here the name is ca, there are three variables in the sequence, each of type char.

Here is an example of calculating the index to the array as the program runs:

    {
     // using indices which are the result of evaluating
     // and expression.

         char ca[3] ;
         ca[0] = 't' ;
         ca[1] = 'o' ;
         ca[2] = 'p' ;
 
         int i ;
         for ( i=0; i<3; i++ ) 
         {
            cout << ca[i] ;
         }
         cout << endl ;  // newline, so it looks nice.

         for ( i=0; i<3; i++ ) 
         {
            cout << ca[2-i] ;
         }
         cout << endl ;  // newline, so it looks nice.
      }

The name of an array not followed by an integer expression enclosed in square brackets is something we have not yet encountered. It is not directly a place to store data. It is a reference to a place to store data. Here is an analogy with luggage lockers in a train station.


The Myth of the Luggage Locker

In Victoria Train Station, London, there are luggage lockers where you can lock away your luggage while you visit the city. You place some money in the vending portion of the locker, this unlocks the locker and gives you a paper ticket to use later as the key. There is a timer, so that you might have to pay more to get your stuff out of the locker if you overstay the original time limit.

A variable is like one of these lockers. In the case of a simple variable, the ticket is unimportant. You refer to the lock by some pet name, like i or j, and you never wander too far from the locker.

An array is a sequence of lockers. You contract for N lockers, int a[N], and to make finding the lockers easy, and to cut down on the number of individual tickets, the lockers are all given in a single row. You only keep track of the first locker in the sequence, and the number of lockers thereafter.

The ticket which references the locker or sequence of lockers is a very interesting item. This ticket is portable. It is much more convienient to walk around town with the ticket rather than the locker. When the locker's contents are needed, the ticket provides the necessary information for the access. This is called dereferencing the ticket. The dereference could be for retrieval of the locker's contents, or the placing of new items in the locker. Moreover, we allow the ticket to be copied. Both the holders of the ticket and the ticket's copy can access the lockers. Tickets can themselves be placed in lockers.


Arguments are passed by value, hence the ticket giving access to the array is to be copied into a variable local to the functions. The type of the variable must match the type of the ticket. In our example, this variable will be of type array of char, or pointer to char, or reference to char, or char-star. Here is an example:

    void printIt(char arrayToPrint[])
    {
        int i ;
        i = 0 ;
        while ( arrayToPrint[i]!='\0' )
        {
            cout << arrayToPrint[i] ;
            i++ ;
         }
     }

     void main()
     {
         char hi[3] ;
         hi[0] = 'h' ;
         hi[1] = 'i' ;
         hi[2] = '\0' ;
         printIt( hi ) ;
     }
The variable arrayToPrint in the function does not contain the array, nor any items in the array. The variable arrayToPrint contains a copy of the ticket allowing access to the array. An access to the array, such as arrayToPrint[i], does the following:
  1. Access arrayToPrint to get the ticket.
  2. Change the number on the ticket by incrementing by i
  3. Access the resulting location.
In the main function, hi is not itself a location. The compiler holds the ticket, and understands that this ticket is indicated where ever the programmer uses the name hi. However, in the main, variable hi[0] through hi[2] are actually created. The the call to printIt is made, a frame is set up, the local variable arrayToPrint of type array of char is created, and the value of ticket hi is written into this local variable.

It is possible to write the function header as:

    void printIt( char arrayToPrint[3] )
    {
       // and so on
    }
Your book suggests this. When an array occurs in the parameters to the definition of a function, the number between the brackets is completely ignored. The compiler knows what's going on: an array of characters is being past into the function. The compiler arranges for a variable of type array of char to receive a copy of the caller's ticket to the actual data elements of the array.

As added clarification:

   void copyIt( char arrayToCopy[] )
   {
       char aRealArray[100] ;
       int i ;
       i = 0 ;
       while ( arrayToCopy[i]!='\0' ) 
       {
          aRealArray[i] = arrayToCopy[i] ;
          i++ ;
       }
       aRealArray[i] = '\0' ;
   }
The declaration of aRealArray does allocate 100 character variables, aRealArray[0] through aRealArray[99]. There is no location aRealArray, it is a ticket name. By the way, the lifetime of aRealArray ends when the function copyIt returns. That is, the array is distroyed. Hence the copy had no useful purpose.