Name: _____________________ Class: CET 375
SSN/ID:   _____________________ Section & Group: ____________
Addresses



"The name of the song is called "Haddocks' Eyes"."

"Oh, that's the name of the song, is it?" Alice said, trying to feel interested.

"No, you don't understand," the Knight said, looking a little vexed.
"That's what the name is called.  The name really is "The Aged Aged Man"."

"Then I ought to have said "That's what the song is called"?" Alice corrected herself.

"No, you oughtn't: that's quite another thing!  The song is called "Ways and Means":
but that's only what it's called, you know!"

"Well, what is the song, then"" said Alice, who was by this time completely bewildered.

"I was coming to that," the Knight said.  "The song really is "A-sitting On a Gate": 
and the tune's my own invention."

                                Lewis Carroll, Through The Looking-Glass
    


A variable data object (i.e., a normal variable) can be thought of as having three components:

  1. Its name
  2. Its address; i.e., the address of the memory location associated with it. Its type, which is the type of value to be stored in its memory location(s), which in turn determines its size, i.e., the number of bytes needed for its values
  3. Its value; i.e., the value stored in its memory location(s)
Up to now, you have probably only used variables from the perspectives of 1 and 3. In this lab exercise, we learn how to find addresses of variables and how to find the size of the various data types.

Getting Started

  1. Start editing a program called address.cpp containing only the following (do this by typing emacs address.cpp & at the unix prompt). This will be your "experimental laboratory."
        /************************************************************
        * Name:  
        * Lab #: 
        * Date:  
        ************************************************************/
    
        #include <iostream>
        using namespace std;
    
        int main() {
            short int si1, si2;
        }
        
    Addresses
    Our first task is to determine the addresses of the memory locations in which the two variables si1 and si2 are stored. This can be accomplished using the address-of operator (&). More precisely, the expression:
    &variable
    produces the address of the memory location i which a variable named variable is stored.

    Note: Addresses may not display correctly on your system. If necessary, use a typecast to make them "typeless" before outputting them:

    cout << ... << (void *) &variable << ...

    void can be used to mean nothing and anything (see this great tutorial (http://vergil.chemistry.gatech.edu/resources/programming/c-tutorial/pointers.html) or this nice tutorial: http://www.augustcouncil.com/~tgibson/tutorial/ptr.html).

  2. Add output statements to your program to display the address of si1 and si2 and record those addresses below:
        si1: ________________      si2: ________________
        
    Memory Maps
    A diagram that shows the association (or mapping) between a program's variable names and memory addresses (given in hexadecimal nottion) is called a memory map. For example, suppose characters are stored in 1 byte of memory and long integers are stored in four bytes of memory. Then, when the compiler processes the declarations:
        long intVal;
        char ch;
        
    it allocates memory for the variables ch and intVal. If the memory locations allocated to intVal are 0x09 through 0x0C (in hexadecimal), and the compiler allocates adjacent memory locations to ch and intVal (from higher to lower addresses), we might picture memory as follows:

    ... 0x08 0x09 0x0A 0x0B 0x0C ...
                 
      ch intVal  

    The memory address associated with intVal would be 0x09, even though it actually consists of locations 0x09 through 0x0C, as indicated by the shaded part of the picture.

  3. After the declarations of si1 and si2, add declarations of:
    1. int variables i1 and i2
    2. long int variables li1 and li2
    3. float variables f1 and f2
    4. double variables d1 and d2
    5. long double variables ld1 and ld2
    6. bool variables b1 and b2
    7. char variables c1 and c2
    Also, add output sttements to display the addresses of si1, si2, i1, i2, li1, li2, f1, f2, d1, d2, ld1, ld2, b1, b2, c1, and c2 and record those addresses below:
        si1: ________________      si2: ________________
        
        i1:  ________________      i2:  ________________
        
        li1: ________________      li2: ________________
        
        f1:  ________________      f2:  ________________
        
        d1:  ________________      d2:  ________________
        
        ld1: ________________      ld2: ________________
        
        b1:  ________________      b2:  ________________
        
        c1:  ________________      c2:  ________________
        
    (If the character address doesn't display correctly, please typecast it to an (unsigned) or (void *), as in the earlier Note.)

    Sketch a memory map of the memory allocated to one or two of these variables (you may use several "strips" of memory if necessary, but be sure to show addresses and variable names):











  4. Question: Does a variable's address change with each execution of a program, or is it the same? What does this indicate about when memory is allocated to these variables (at run-time or at compile-time)?




  5. From these displayed addresses, we can determine how many bytes are used to store a value of a particular type. For example, if variables t1 and t2 have declarations of the form
    T t1, t2;
    and the address of t1 is a1 and the address of t2 is a2, then values of type T are stored in a2 - a1 bytes (or a1 - a2 bytes if memory is allocated from lower to higher memory addresses). Show how this can be done for type int; that is, show how we can determine the number of bytes used to store an int value.

    Note: Addresses are typically displayed in hexadecimal (base 16) notation, so you may have to do some hexadecimal-to-decimal conversion or do your calculations using hexadecimal arithmetic. (You might be able to have the program do this for you -- try casting the addresses to type unsigned.)




Use similar calculations to determine how many bytes are used to store a value of each of the following types:
short int: ________________      int: ________________      long int:    ________________
float:     ________________   double: ________________      long double: ________________
bool:      ________________     char: ________________ 
The sizeof operator, which has two common forms,
sizeof(Type) or sizeof(variable)
produces the number of bytes allocated for an object of type Type, while the second gives the number of bytes allocated for an object named variable. Using one of these forms, verify the correctness of your answer to the last question.


Dereferencing
The value stored in a memory location can be accessed using the dereferencing operator *. Show this by putting the following statements i your program (see the earlier note on void *).

si1 = 12345;
cout << "si1 = " << si1 << endl
     << "Address of si1 = " << &si1 << endl
     << "Contents of " << &si1 << " = " << *(&si1) << endl;
What output is produced?




Hand In: This lab handout with the answers filled in attached to a listing of your final Makefile
(use the enscript command from your Programming Style Sheet to print it out:
enscript -E -G -2rj -M Letter -PECT2_PS <filename>. You can also use a2ps as in: a2ps <filename>).


Ricky J. Sethi <rickys at sethi.org>
Last modified: Mon Mar 28 18:30:09 PST 2005