This email is best viewed with a fixed width font like Courier. I'll put it up on the web site: http://www.cs.washington.edu/education/courses/cse143/CurrentQtr/AH/ It's also really, really, really, really long. Really, really. You should print it out and read it offline, so you can refer to earlier stuff as you read along.
// for you HTML viewing folks Well, I think I failed at the CS Hippocratic Oath to "... avoid harm." I'll see if I can give some more examples and code that may help clarify the memory, pointers, arrays thing. I'll assume the following classes: class Blob { public: Blob(); Blob(int val); void update(int change); int value(); private: int n; }; class WordList{}; // same idea as homework class Point { public: Point(); Point(int x, int y); int x, y; }; You declare a variable of a certain type: int x; char input; Blob bubba; WordList the_list; Point myp; Blob arrayb[10]; If you declare an instance of a class, as above, then the default constructor is invoked. In particular, if you declare an array of objects (classes) each of the elements of the array will have its constructor called. So for arrayb, all 10 will have their default constructors called, so arrayb[3].value() is 42. To invoke a different constructor, you specify it at the time that you declare the variable: Blob fred(13); Point ap(10,17); These variables must exist someplace in memory. They are created on what is called the stack. You declare a pointer of a certain type using the *: int *p; // pointer to an int char *ch; // pointer to char Blob *bp; // pointer to a Blob Point *pp; // pointer to a Point Since they are variables, they must also exist somewhere. They are created automatically as well, and exist on the stack. Now, if you assign a pointer, you must give it a pointer. Just like to store a character, you must give it a character. Moreover, you must assign a pointer to a pointer of the same type. The & creates a pointer to a variable. So: int *x; // int pointer int j; // plain old int j = 14; // assignment x = &j; // &j gives as an int pointer which we assign to x p = x; // p and x are both int pointers Pictorially: x j ____ ______ | ----------> | 14 | ---- ------ ^ p | ____ | | --------------- ---- Now both p and x point at j. What is going on behind the scenes is that what the & does is it gets the actual memory address of the variable j. It stores this value, the memory address of j, into x. Then when the assignment p = x happens, p gets a copy of the value stored in x, which is the address of variable j. In essence, p and x now point to the same thing, j. Similarly, you could do: Point usefulp; pp = &np; Now pp, which is a Point pointer, has the address of the variable usefulp. More importantly, we can think of pp as pointing to usefulp. Note that the creation of np above did not create an actual Point (like usefulp). It created a pointer, which just stores an address. pp usefulp ____ _________ | ------->| | ---- --------- So far, all of the memory has been done on the stack. This has certain drawbacks. If we want an array: int myints[100]; It has to have a fixed size. If we had Dynamic Memory, we could change the size of the array as we needed. Dynamic memory is created on the heap. We can create an object of any type using the keyword new. So if we have: int *myi; // this is an uninitiazlied pointer myi _____ | -------> ??? ----- it doesn't actually point at anything. But we can change that by doing: myi = new int; new creates an anonymous instance of the type, and returns a pointer to it. So the pictures is now: myi _____ _____ | --------> | | ----- ----- Notice that the memory on the right doesn't have a name. Now, remember how behind the scene pointers store an address of memory. Well, that means that myi stores the address of that block of memory created somewhere on the heap. You can create a new object of any time, so we can do: ch = new char; // ch points at a char bp = new Blob; // bp points at a Blob pp = new Point; // pp points at a Point This is the same things as doing: Blob b; Blob *blobp; Point p; Point *pointp; blobp = &b; pointp = &p; Except that if we use new, as above, then we don't have an extra variable name cluttering up our code (b, p don't exist if we just do the new as with bp and pp). To access the members of a pointer to an object, you do the same thing as with pointers to structs, using the -> notation: cout << bp->value() << endl; // call value of bp, prints 42 blobp->update(4); // add 4 to blobp's value pp->x = 4; // set the public data member // of pp to the value 4 Note that if you create a new object, then it's default constructor will be invoked: bp = new Blob; // default constructor for a Blob is called bp->value(); // value 42 If you want to invoke a different constructor you can do: bp = new Blob(14); bp->value(); This will invoke the overloaded constructor, which gives n the value 14. (n is a private data member of bp). As motivation, we wanted to be able to create dynamically lengthed arrays. How do we do this with arrays? Well, we declare an array as: int myarr[100]; This creates 100 consecutive ints on the stack. We access them: myarr[2] = 4; We can access the array through a pointer. Remember, that each individual element of the array has the type of the array (in this case int). x = myarr[2]; // x is now 4, both x and myarr[2] are ints p = &x; // p points to x, &x is a pointer to x p = &myarr[2]; // p points to the 3rd element of myarr, // which is an integer. &myarr[2] is // the address of the 3rd element of myarr In slightly greater detail: myarr[2] is an integer, so it must live some place in memory, and that place has an address. &myarr[2] gets the address, which is the same as creating a point to myarr[2], and we then assign it to p. In C++, myarr (without the []) is the address of the first element of the array. So the following: if (myarr == &myarr[0]) cout << "Good"; will print out Good. Therefore, we can think of myarr as a pointer to the first element of the array, and so if myarr[0] makes sense, then p[0] works the same way. However, the assignment above to p makes it such that p[0] == myarr[2]. I think Hal will talk more about this in class on Friday. With this understanding of the similarity between pointers and arrays, we can now understand the following: int *ip; ip = new int[15]; ip is a pointer to the first element of the anonymous array which is created on the heap. That is, ip is a pointer to the array. We can use ip like an array, so ip[3] is the 4th element of the array. Remember that what new returns is a pointer (an address), so in this case, new returns a pointer to the block of memory that starts the array. Similarly we can do: bp = new Blob[12]; pp = new Point[17]; Remember, that since these are arrays of classes, that the default constructor will be called on each of the elements of the arrays. So 12 Blob default constructs, and 17 Point default constructors are called. Lastly, the best part about dynamic memory is the dynamic part. This means that you can decide how long you want arrays at run-time, not statically at compile-time. For example: int length = 20; ip = new int[length]; I can specify a variable inside the [] brackets of new, and that will be how many ints I get. You access them as before, ip[8] = 14; Similarly, you could do: cout << "How many? "; cin >> length; Blob *pblobs; pblobs = new Blob[length]; And then you use the individual ones just like a normal array of classes: pblobs[4].update(3); // or cout << pblobs[2].value(); There was a question today about why we can do things like: Blob *bc = new Blob; This is just a special shorthand notation for: Blob *bc; bc = new Blob; The thing on the left is a declaration of a pointer, and the thing on the right is a pointer, so those types are the same. Don't get confused between declaring a pointer (char *ch; and dereferencing a pointer, *ch = 'A'; which both use the *). You could NOT do: *bc = new Blob; You could, but would never, ever want to, do something like: Blob ed; ed = *(new Blob); I'm hoping that these explanations will do more good than harm. There are clearly some open questions, like how do I delete memory I created (use delete: int *xx = new int; delete xx;) which will be covered soon. Please come talk to me, or any other TA, if you're still unclear, and want more help. -Justin PS: Here's a little program that you should look at and run, and think over. #includeclass P { public: int n; }; int main() { int x = 4; int *p; int *q; p = &x; q = p; cout << "x: " << x << endl << "address of x: " << &x << endl << "p: " << p << endl << " value of what p points to: " << *p << endl << "q: " << q << " val q: " << *q << endl; int arr[100]; arr[0] = 4; if (arr == &arr[0]) { cout << "Good" << endl; } cout << *arr << endl; P *pp; pp = new P[10]; pp[0].n = 14; return 0; }