Please send questions to st10@humboldt.edu .

CIS 130 - Week 14, Monday - April 26, 2010

*   Sometimes people categorize parameters into the following 3 categories:

    input parameters
    ...what almost all of our functions with parameters have
       used this semester! They provide information INTO the
       function from the caller;

       C++ assumes that most (scalar) parameters are input parameters.

    BUT there can be other categories as well:
    output parameters
    ...these we haven't said much about yet!
       These are parameters we intend to set within the
       function, and we would LIKE the corresponding
       argument to be changed accordingly;

       You have to generally tell C++ if you want this...

    input/output parameters
    ...we haven't said much about these, either, yet;
       These are parameters that provide information INTO the
       function, and ALSO may be modified by the function,
       and we would LIKE the corresponding argument
       to be changed accordingly;

    ...in general, you NEED to make sure that the user knows
       if a parameter is an output or an input/output parameter,
       as those are both more unusual AND they require more
       careful action by the user;

FIRST: how C++ passes (scalar) input parameters
*  it uses PASS-BY-VALUE (sometimes called CALL-BY-VALUE)

   *   int double_me(int amount)
       {
           return amount * 2;
       }

       for example,
       double_me(3) == 6
       double_me(2 + 7) == 18
       double_me( double_me(5) ) == 20 

       *   when you call double_me,
           a memory location is set up for parameter amount,
	   and the value of the argument expression is COPIED
	       into that amount memory;

*   to see if you're really getting this, consider this
    UGLY function with POOR STYLE

    int ugly(int value)
    {
        value += 30;

        return value;
    }

    MAKE SURE YOU SEE:

    if ugly is called as follows:

    int quant;
    quant = 4;
    cout << ugly(quant) << endl;
    cout << quant << endl;

    ...that this fragment would cause the
       following to be printed to the screen:

    34
    4

    ...because, with pass-by-value, you CAN'T muck with the
       argument even if you muck with the parameter;

       it is "safe" for true input parameters;

    (and because it is thus potentially misleading to
    a casual user, it is considered POOR STYLE to muck
    with a pass-by-value parameter -- it is intended 
    to be used just for input, after all;)

With scalar pass-by-value parameters, you cannot muck with the argument
    by just mucking with the parameter;

*   what do you do if you would like to change an input parameter
    in a function?
    ...copy it into a local variable in that function,
       and change that instead;

    int less_ugly(int value)
    {
        int value_copy;

        value_copy = value;

        value_copy += 30;

        return value_copy;
    }

NOW -- why do I keep specifying SCALAR above?
*   ...because, to avoid copying who-knows-how-many elements
       that can be in an array when an array is a parameter,

       C++ instead just copies over WHERE the array the starts;
       (it copies the address of the array...)
       (where to find it)

*   It copies the array by value, but for an array, what
    it copies is just the address of its first element!

    Changing that address in the parameter won't where the
    argument array starts --

    BUT writing parameter array expressions that change the 
    array can really change the values AT that argument's
    address;

    ...changing the parameter array DOES change the argument
       array.

    (arrays, in C++, are automatically input and output
    and input/output parameters...!)

*   (I think you can use const in a function header to indicate
    to the C++ compiler that an array parameter must not be
    changed... but that's kind of beyond the scope of the
    course...)

That's still pass-by-value, though.

C++ has another passing parameters, too:
PASS-BY-REFERENCE (or call-by-reference)

*   ...and with pass-by-reference, you can set up scalar
    parameters to be output or input/output parameters, too;

*   when you pass a parameter by reference,
    you don't copy the value of the argument into the parameter,
    you copy a REFERENCE to the argument -- or, its address!

    (if you passed an array by reference, you would copy the
    address of its address into the parameter...! but that's
    also beyond the scope of CIS 130...)

*   you have to TELL C++ when you want to pass by reference;
    you do that with this syntax:

    return_type funct_name(param_type& param_name, ...) 
                                     ^ put this & after EACH parameter's
                                       type for EACH pass-by-reference
				       parameter;

    THAT tells C++ to pass THAT parameter by reference --
        to copy over the argument's address into that parameter,
	and when you change that parameter, to thus change
	   the value AT the argument's address -- to change the argument!!

        *   so, with pass-by-reference, changing the parameter DOES
	    change the argument!
            ...you use pass-by-reference for output and input/output
	       parameters, then;

THE classic pass-by-reference example:

swap!
/*
contract: swap: int& int& -> void
                   ^ PUT that it is pass by reference in your contract
		     by putting & after that parameter's type!!

purpose: expects two integer arguments, and returns nothing,
         but has the side effect that when it is done,
	 the first argument has the original value of the second
	 argument, and the second argument has the original value of the
	 first argument;

void swap(int& value1, int& value2)

examples: 
         int quant1 = 15;
         int quant2 = 27;
         swap(quant1, quant2);

         ...then, after this call:
	 quant1 == 27
	 quant2 == 15

void swap(int& value1, int& value2)
{
    int temp;

    temp = value1;

    value1 = value2;
    value2 = temp;
}

*   this works!

...but note: pass-by-reference arguments are RESTRICTED, compared
       to pass-by-value arguments;
       ...pass-by-reference arguments have to have addresses,
          they have to be ABLE to be reasonably changed!

	  swap(3, 5);   <--- ILLEGAL!
          swap(a+1, b+2) <-- ILLEGAL!

          const int NUM_THINGS = 7;
          int quant = 15;
          swap(NUM_THINGS, quant); <--- ILLEGAL!

       (notice that you can pass two array references --
        they have addresses...

          swap(arr[1], arr[3]);  <-- is legal...

*   then, you should choose the right parameter-passing method
    for your parameters!

    a scalar input parameter? use pass-by-value
    an array input parameter? figure out where that const goes...

    a scalar output or input/output parameter? use pass-by-reference

    an array output or input/output parameter, where the location of the array
    won't change? pass-by-value of that array will work fine;

    (OK, and if the location of the array MIGHT need to change?
    THEN you'd pass the output or input/output array parameter
    by reference...!)