PASSING VARIABLES BY REFERENCE
==============================
*************** example function 1 **********************
void fun (int a, int b)
{
mixed c;
}
********************************************************
a,b: [argument variables-Mateese creation] formal parameter
c : local variable
Mateese says: The only difference between them is in the way they get
their initial values.
Mateese says: When the function is called, the driver creates enough
space for these three variables, initialises them somehow, and lets
the function loose on it.
Mateese says: The space for this variables exists only for this function
- so nothing the function does to the variables is visible outside.
Mateese says: Consider the call: fun(2, 3).
Mateese says: When calling the function, the driver creates the space
for the variables and initializes them.
Mateese says: Variable c is local, it is initialised to 0. Variables a and b
are argument variables, so their values are taken from the given arguments,
namely 2 and 3.
Mateese says: A more complicated call now. We have in the caller a
variable 'd':
**************** example function 2 *******************************
fun2() {
d = 3;
fun(d,4);
}
fun(int a, int b)
{
mixed c;
}
*******************************************************************
Mateese says: What happens here is that the driver first replaces the
variable 'd' in the arguments by its value (it is said to copy the value),
yielding fun(3,4) as call.
Mateese says: The rest is as usual: the space for fun's variables is
allocated, a and b are initialized from the arguments to 3 and 4.
Mateese says: Now the tricky part of references...
Mateese smiles demonically.
************************* example function 3 *********************
fun2() {
d = 3;
fun(&d, 4);
}
fun(int a, int b)
{
mixed c;
}
*******************************************************************
Mateese says: This time, the driver recognizes the & symbol and does _not_
replace the variable by its value.
Mateese says: Instead, when allocating the space for fun's variables,
it just creates enough space for the variables b and c.
Mateese says: For the argument variable a, the space of the caller's d is
reused, and with it the value which happens to be in it.
Mateese says: So, if fun() assigns any value to 'b', this takes place in its
own space.
Mateese says: But if fun() assigns anything to 'a', the space owned by the
caller to hold its variable 'd' is used - any change to a within fun() will
therefore also affect variable 'd'.
say so it goes upwards? I change a in fun(), and whatever called it
also gets changed?
Mateese says: Correct.
Mateese says: Take a look at this:
********************* example function 4 ***************************
void fun (int a, int b)
{
mixed c;
a = 2;
b = 1;
c = -1;
}
void caller ()
{
int d, e;
d = 8;
e = 5;
fun(d, &e);
}
*********************************************************************
Mateese says: What values do d and e contain, and why?
say the caller calls fun, puts 8 into d? but then d becomes a,
which is set to 2
say but d in caller doesn't change
Mateese says: Right.
Mateese says: Why?
say a becomes 2, but since it's not passed by reference, it only
changes in the second fun, not the calling fun
say which on the other hand, e is called by reference.
Mateese nods.
say it is initially 5 in caller(), gets sent to fun(),
put into b. Then b gets changed in fun()
say so both b and e are now 1
Mateese says: Right.
Mateese says: Another way to put it (you will read it eventually somewhere)
also contains hints about what happens.
Mateese says: d is passed 'by value': the driver creates fresh space for the
variable a and copies the _value_ from d into a.
Mateese says: e is passed 'by reference': the driver does not create fresh
space for b, but instead lets b _refer_ to the space of e.
Mateese would like to explain what happens if you pass to many or not
enough arguments.
Mateese says: It's really simple, compared to references.
Mateese says: Consider a slightly different function to call:
******************** example function 5 ******************************
void fun (mixed pl, int level, int qp, int age)
{
if (stringp(pl))
pl = find_object(pl);
level = pl->QueryLevel();
qp = pl->QueryQP();
age = pl->QueryAge();
}
*********************************************************************
Mateese says: Ignore the function body for now.
Mateese says: The proper way to call this function is:
fun(name, &lvl, &qp, &age).
Mateese says: (Believe me that for now).
Mateese says: If you call it this way: fun(name), the driver expands this
internally to: fun(name, 0, 0, 0) and then performs the call.
say why?
Mateese says: Because the function has four argument variables, which can't
suddenly cease to exist just because of some stupid caller.
Mateese says: So the driver compromises and allocates the space for these
variables and initialises them to 0.
say ah...but if they are 0's, are they still variables?
Mateese says: Right.
say can I still use them?
Mateese says: Within fun? Yes.
Mateese says: The not-given arguments are treated as if passed by value,
the value being 0.
Mateese says: The whole by-value/by-reference business is under control
of the caller - it's his fault if he gives you his variables by reference,
free for mutilation.
---------------------------------------------------
fun(name) === fun(name, 0, 0, 0)
fun(name, lvl) === fun(name, lvl, 0, 0)
fun(name, lvl, qp) === fun(name, lvl, qp, 0)
fun(name, lvl, qp, age) === fun(name, lvl, qp, age)
but note:
fun(name, qp) === fun(name, qp, 0, 0)
---------------------------------------------------
Mateese says: Too many arguments are even easier: all extra arguments are
ignored
----------------------------------------------------------------------
fun(name, lvl, qp, age, foo, bar, baz, etc) === fun(name, lvl, qp, age)
-----------------------------------------------------------------------
Mateese says: So, I did chose this latter function with purpose:
it takes the name of a player or the player object itself as argument,
and puts some information from it into the other argument variables.
Mateese says: In your case, you have the player name available, and are
interested in the number of qp he has.
Mateese says: Lets say, the name is in the caller variable 'who', and
you want the QP amount be placed into the variable 'qp'.
Mateese says: The proper call to fun is now: fun(who, 0, &qp)
Mateese says: The first argument, who, is clear: the function needs
the player name to operate on.
Mateese says: We don't want our variable to be changed accidentally, so we
pass it by value.
Mateese says: But we want the function to return the QP amount into qp,
so we pass the qp variable by reference.
Mateese says: Between the name and the qp, the function expects the
lvl-variable, which we aren't interested in, so we just provide a dummy,
the value 0. The age variable comes last and can safely be omitted.
say ok...Q: if the fun gets called in /std/guild, how could the player
name get changed?
Mateese says: Look at the start of the function:
void fun (mixed pl, int level, int qp, int age)
{
if (stringp(pl))
pl = find_object(pl);
say and that could change the poor player's name?
Mateese says: If we call the function and pass a _name_ 'by reference', the
function would change the callers variable.
Mateese says: No, the player's name wouldn't be changed, but the caller will
run into trouble.
Mateese says: Example:
********************** example function 6 ****************************
void caller()
{
string who;
int qp;
who = "any name";
fun(&who, 0, &qp); // fun() changes who from a string into an object!
write("Player "+who+" has "+qp+" QP.\n");
// runtime error: who is not a strg
}
*********************************************************************
Mateese says: So, unless you really _want_ your variables be changed, pass
them by value only :-)