Pointer, Array, String Review


Here, we review how to use basic pointers and arrays. We do not describe everything one might want to do with pointers or arrays.

  1. Pointers to single objects:

    The complete code we use in this section is available as ex1.c.

    Suppose a main() function (or any other function) defines the following variables:

    int a = 8, b = -2;
    int *ip;
    

    These would look something like the following in memory:

    a @1000    b @2000
    ------     ------
    |  8 |     | -2 |
    ------     ------
    
    ip @3000
    ------
    |    |
    ------
    

    I.e., each variable is located at a certain point in memory (e.g., suppose a is at location 1000, b at location 2000, and ip at location 3000).

    The variables a and b have initial values of 8 and -2 respectively. The pointer ip has an arbitrary initial value.

    Now, what would each of the following statements do:

    Now, suppose we have two other functions in this program:

    void foo(int c)
    {
      c += 5;
    }
    
    void bar(int *jp)
    {
      *jp += 8;
    }
    

    What would happen when we performed each of the following statements in our main() function:

  2. Pointers and Arrays:

    The complete code we use in this section is available as ex2.c.

    Suppose a main() function (or any other function) defines the following variables:

    double ar[4] = { 1.2, 3.5, -2.0 };
    double *dp;
    

    These would look something like the following in memory:

    ar @6000
    ---------------------
    | 1.2| 3.5|-2.0| 0.0|
    ---------------------
    
    dp @7000
    ------
    |    |
    ------
    

    I.e., the array ar has room for 4 elements (as its size indicated). Since only 3 initializers are given, the 4th position gets initialized to zero. The pointer dp is meant to point to the kind of things in the array, doubles.

    Now, what would each of the following statements do:

    Now, suppose we add a function to print out all the values in a double array:

    void PrintEm(double br[], int n)
    {
      int i;
    
      for (i = 0; i < n; i++)
        printf("%f\n", br[i]);
    }
    

    Some things to note about PrintEm():

    Now, to pass ar to PrintEm(), we'd do:

    PrintEm(ar, 4);
    

    Since ar is an array and the function wants an array, it makes sense to just pass it along. When PrintEm() is called, its parameter br will come into existence:

    Scope of main(): Scope of PrintEm():
    ar @6000
    ---------------------
    | 5.7| 3.5| 0.0|11.1|
    ---------------------
    
    dp @7000
    ------
    |6000|
    ------
    
    br @8000
    ------
    |6000|
    ------
    

    You'll note however that br is not an array like ar. It's actually just a pointer, like dp. (Since it's really just a pointer, that is why we didn't have to give br an array size.)

    This illustrates the different between using array notation to declare an array as a parameter versus as a local or global variable. When you do something like:

    int array[10];
    

    as a local variable (in a function) or as a global variable, you really get an array. When you use that notation inside of a parameter list:

    void SomeFunc(int array[]);
    

    you only get a pointer. I.e., arrays are always passed by reference, automatically.

    So, it makes sense that we could just pass the array as:

    PrintEm(ar, 4);
    

    since the name of an array, like ar, acts like the address of its first element in certain contexts...an address was exactly what PrintEm() was expecting since its parameter br really is just a pointer.

    Now, let's perform some more statements:

    Now, we said that the parameter br in PrintEm() is really just a pointer. So, we can write a second version of this function, PrintEm2() that makes that fact explicit:

    void PrintEm2(double *br, int n)
    {
      int i;
    
      for (i = 0; i < n; i++)
        printf("%f\n", *br++);
    }
    

    We now explicitly list the parameter as a pointer. Also, instead of using subscripting, we use some pointer arithmetic.

    How should you read *br++?


    Answer: Since there are no parentheses this time, you must go with operator precedence. But, * (dereferencing) and ++ (here, the post-increment) have the same precedence. So, you must go with associativity--they associate from right to left.

    So, the first part to be done is "br++", which says to increment the pointer. But wait, its a post-increment, so we use it first... I.e., we use the variable to do "*br", which means to use the double that br points to. Finally, we can do the post-increment and advance the pointer.

    So, the expression "*br++" really means:

    1. Use what br points to (i.e., print out that double).
    2. Increment the pointer (i.e., make it point to the next double).

    In summary, we now only use i to figure out how many elements to print. We use pointer arithmetic to advance from one element to the next.

    We can now call this new version, PrintEm2(), passing along the array. Here are some statements that do that:

  3. Pointers and Strings:

    The complete code we use in this section is available as ex3.c.

    Although strings are stored in arrays, and thus, work like our array examples above, strings have a few special features:

    Now, suppose a main() function (or any other function) defines the following variables:

    char str[] = "cs113";
    char *sp;
    

    These would look something like the following in memory:

    str @9000
    --------------------------
    | c | s | 1 | 1 | 3 | \0 |
    --------------------------
    
    sp @10000
    ------
    |    |
    ------
    

    Note that the array has 6 elements, an extra one for the nul character.

    Now, what would each of the following statements do:

    Now, suppose we add a function to print out the string (of course, printf() can do this already, but we'll do it ourselves character by character):

    void PrintStr(char astr[])
    {
      /* call strlen just once (more efficiency) */
      int len = strlen(astr);
      int i;
    
      for (i = 0; i < len; i++)
        printf("%c", astr[i]);
    
      printf("\n");
    }
    

    Some things to note about PrintStr():

    Now, to pass str to PrintStr(), we'd just do:

    PrintStr(str);
    

    When PrintStr() is called, its parameter astr will come into existence:

    Scope of main(): Scope of PrintStr():
    str @9000
    --------------------------
    | c | s | 1 | 1 | 3 | \0 |
    --------------------------
    
    sp @10000
    ------
    |9000|
    ------
    
    astr @11000
    ------
    |9000|
    ------
    

    Again, astr is not an array like str, but just a pointer.

    Let's now write a second version of the printing function, PrintStr2() that takes advantage of this pointer:

    void PrintStr2(char *astr)
    {
      while (*astr != '\0')
        printf("%c", *astr++);
    
      printf("\n");
    }
    

    We've already used notation like "*astr+++" before. Moreover, this new version no longer needs i, len or strlen() to count until the end of the string. Instead, we just advance a pointer from one character to the next until we get to the nul character (\0).

    We can now call this new version, PrintStr2(), passing along the array. Here are some statements that do that:

  4. Final question:

    So, pulling all that you have learned together, consider the following variable definitions:

    int m;
    float fa[3];
    char s[10];
    

    How would you complete the following scanf() to read in: a value for m, the 2nd element in fa, and a string value for s?

    scanf("%__%__%__", __________, ___________, ___________);
    


    BU CAS CS - Pointer, Array, String Review
    Copyright © 1993-2000 by Robert I. Pitts <rip at bu dot edu>. All Rights Reserved.