|
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:
- Its name
- 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
- 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
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).
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.
After the declarations of si1 and si2,
add declarations of:
- int variables i1 and i2
- long int variables li1 and li2
- float variables f1 and f2
- double variables d1 and d2
- long double variables ld1 and ld2
- bool variables b1 and b2
- 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):
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)?
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