CS 112 - Week 6 Lecture 2 - 2022-09-29

TODAY WE WILL
*   announcements
*   STOP 4 - C++ pointer basics
*   STOP 5 - dynamic memory
*   [not reached yet]
    [oops, aside! some useful syntax:
    pointers to objects
*   [if time] STOP 6 - dynamic memory and
    arrays
*   prep for next class

*   Current reading:
    Savitch Ch. 9 - pointers and dynamic memory

=====
STOP 4 - pointer basics!
=====
*   what is a pointer?
    Savitch, an old edition of our text:
    "A pointer is the memory address of a variable"

*   int x = 3;
    cout << x << endl;

    Well -- when x is a pointer --
            it contains the ADDRESS of something
	    else,
	    and it can be thought of as
	    "pointing to" the thing AT that address;

    *   when you set a pointer,
        you can be putting into it the
	address of the thing you want to point to

    *   In C++,
        a pointer type INCLUDES that type
	of the value at the address in the pointer;
	that is, it includes the type it points to;

        so, pointer-to-int, or pointer-to-double

   *   what is the syntax for this, then?
       to declare a variable to be of
       type pointer-to-some-type?

       In C++, you indicate a pointer variable
       by putting an asterisk between the
       type-pointing-to and the variable name

       int *quant_ptr;
       double *num_ptr;

*   like any variable in C++,
    just declaring it does not generally
    give it initial values -- it is considered
    undefined
    *   it is considered good STYLE to initialize
        any declared variable before you use it

    *   it you don't want a pointer variable
        to point to ANYTHING yet,
	one common value to make it point to
	is NULL
	*   NULL is a named constant (I *think*
	    defined in cstdlib)

            Any pointer CAN be set to it.

            THEN a COMMMON action is to make
	    sure a pointer is NOT NULL before
	    you follow it!

	    if (my_ptr != NULL)
	    {
	        ...follow my_ptr...
            }

     *   what if I want to point to something?

         OUR FIRST way:
	 to use & as a prefix operator!

         val
	 &val <-- the address of val

    double my_double = 43.5;
    double *my_dbl_ptr;

    // set my_dbl_ptr to the address of my_double,
    //    and now it can be set to point to my_double

    my_dbl_ptr = &my_double;

    ^ I tend to read this as
      "set my_dbl_ptr to the address of my_double"

*   can I get the thing AT the address within a pointer?
    that is, can I get the thing the pointer is
       pointing TO?

    YES -- use the * as a prefix operator in that
    case!
    * desired_ptr -> the value at the address within
                     desired_ptr
       
    cout << (*my_dbl_ptr) << endl;

*   If you change the value at the address being
    pointed to, and then follow the pointer again,
    it sees the new value

    my_double = 4008;
    cout << (*my_dbl_ptr) << endl; // print 4008

*   you are allowed to CHANGE what is AT an
    address using this syntax!!!

    *my_dbl_ptr = 32;
    cout << "my_double: " << my_double << endl;

    ...prints:
    my_double: 32

*   BUT -- pointers are MORE TYPICALLY used
    with DYNAMIC memory, memory allocated
    by the programmer while the program is
    running;

    *   how can I write a statement requesting
        dynamically-allocated memory?

        ...the operator new

        new desired_type
	...allocates a chunk of dynamic memory
	   of the size good for desired_type
	   and returns the ADDRESS of that
	   dynamic memory chunk

        new double   // allocate a double-size chunk
	             //    and return its address
        new int      // allocate an int-size chunk
	             //    and return its address

    ...this is GREAT to assign to a pointer!

    int *num_ptr;

    num_ptr = new int;
    *num_ptr = 13;

*   BUT: what a programmer dynamically allocates
    using new,
    they are expected to DEallocate using:
    delete

    delete <desired_addr>;
    delete desired_ptr;

    delete num_ptr;
    num_ptr = NULL; // if program is not ending now!

    // basically, unless the program or function is
    //     about to end, either set the pointer
    //     pointing to deallocated memory to
    //     NULL or to whatever it is intended to point
    //     to next -- don't leave it pointing to
    //     the deallocted memory