- call by value - call by reference - call by sharing - call by value/return
Call by value: function were calling receives a copy of the value we are passsing Call by reference: doesnt receive a copy, receives a pointer to a position in memory Call by value/return copies the value sent, modidfies it, and if everything goes okay, sends it back to the caller
Call by Value:
- a copy of the value is passed to the function. changes to this value do not effect the value of the variable in the caller. - if foo calls bar, both foo and bar's stack frames have a copy of variable. changing var in bar does nothing to the variable stored in foo's stack frame. in c: call by value is the only parameter passing mode. All other modes can be achieved in C using pointers as a building block.
Call by reference:
- the address of the argument is passed - callee's formal parameter is an alias of the caller's actual parameter - passing a pointer to a to bar. through that pointer, bar can manipulate a. - changes to the callee's formal parameter affect the caller's actual parameter, - the actual parameter must be an l-value l-value is really a memory location. always assigning an r value to an l value by saying, put this r-value in this memory location. Something that has an address. - passing a pointer to that memory location to the function call in c:
void foo() { Shape a = makeCircle(GREEN); bar(&a); }
-a copy of the var is passed - at the end, the formal parameter is copied back to the actual parameter. - if everyting goes well, this way works just like pass by reference. - the difference is, if the function throws an exception, the original state of the caller is preserved. - changes to the callee's formal parameter (eventually) effect the caller's actual parameter. - leaves caller's actual parameter in a constant state if the callee exits abnormally.
void foo(){ Shape a = makeCircle(GREEN); bar(&a); } void bar( Shape *ref){ Shape x = *ref x.color(RED) x = makeSquare(CYAN) *ref = x // this never happens if an exception occurs along hte way }
ADA uses call by value/return. Call by value/return is basically a SAFE version of call by reference. ADA was invented by NASA to code rockets, so safe code was pretty important.
Call by Sharing
is really nothing but using call by value for variables that use a reference model ? cant remember if that's what he said - In java, creating an object means it is put in the heap, and the variable stores a pointer to it. - Calling a function means putting the value of a in x. but they are both pointers to the same memory. - changing the color of x also results in changing the color of a because they point ot the same thing - saying x = new square means that x no longer points to the same thing a does and the link is broken.
foo(){ a = greenCircle bar(a) }
bar(x){ x.color(red) x = blueSquare }
In C: This leaks memory since the reference to a's memory is detroyed without being freed
FORTRAN: - all parameters are passed by reference - temporary variables are used to pass non-l-value expressions (eg. passing an actual int value)
Pascal: - call by value is the default - keyword var before formal parameter switches to call by reference. eg. \procedure sub(a: integer; var b: integer). b gets passed by reference.
C: - call by value - array are passed by value, as pointers (arrays are represented as just pointers to the first array element anyway) - to simulate call by reference, pass a pointer.
C++: - same as C but with the addition of reference parameters void swap(int &a, int &b) { int t = a; a= b; b = t;} pointers have to be dereferenced to be used. references automatically dereference themselves when you use them. (introducing references was a mistake - zeh) - references can be declared const: efficiency of call by reference and safety of call by value
Smalltalk, lisp, Clu, ML - reference models of variables - call by sharing: object can be altered, just as with call by reference, but the identity of the object cannot change.
Ada: - in parameters: call by value - in out parameters: call by reference or call by value/return - out parameters: call by result implemented like call by reference,
Java/python: - call by value for primitive types - call by sharing for compound types (objects)
C#: skipped
Read-Only Parameters:
A common practice in pascal:
when we have large values, if we want to pass by value, that involves a lot of copying. and sucks. as a result it was common practice for pascal programmers to choose explicitly, if an object was a certain size, pass it by value/reference depending on which is faster. this has a high potential for bugs since the callee can change stuff in the param.
to get around this, read only parameters. Passa reference, but the callee isnt allowed to change anything in it. efficiency of CB ref, safety of CB val
Modula 3: readonly params
ANSI C, C++: const parameters.
When using call by value, declaring a paremeter readonly or const is pointless
Default (optional) Parameters
These need not be specified by the caller. If not specified, they take default values.
ADA: procedure put( item : in integer; width : int field := 10);
C++: void put(int item, int width = 10) {...}
Named (keyword) parameters
these need not appear in a fixed order. - good for documenting the purpose of parameters in a call - necessary to utilize the full power of default parameters
ADA: format_page(column => 2, width => 4, font => Helvetica);
Compiler, at runtime, basically just rearranges this to pass the arguments in order like a normal function call.
Subroutine closures as parameters:
Functions as parameters and function return values require the passing of closure.
f::Int -> (Int -> Int) fx = \y -> x + y
--f's "closure" is actually a pair representing two things: the implementation of \y = x + y, and the context of f. (f's frame)
As soon as we allow functions to return functions, those functions risk escaping the scope in which they were defined. We have to make sure they can access things outside of their scopes. You need to not only pass a pointer to the function, but include the context within which the function was called, called a stack closure.
In C, C++ and Fortran, functions are not allowed to nest, but pointers to subroutines suffice.
Variadic subroutines (variable number of arguments)
c/c++/python allow variable numbers of arguments.
~listen to recording~ - Has a ... to indicate a variable number of arguments after the required ones. - required arguments are type checked but not the optionals - uses va_start, va_arg, and va_end - va_start initializes an internal pointer to the first argument after the last required one - pull out an arg with va_arg, which also takes the expected type as a second arg - va_end destroys the struct that was used to walk through this list - things that can go wrong: try to acccess more args than were provided, and end up outside the stack frame. both the function implementer and the caller need to make sure that theyre using the function arguments correctly. sometimes this makes sense but its risky because there are no safety nets - java nd C# also allow it but its more restrictive. syntax to specify that function is variadic array of extra args since its an array, all the variables have to be the same type. safer, since you know the type and you cant walk outside any bounds. - is there a way to make java allow different types? yeah because you can pass objects and cast them. - variadic args are pushed below everything else on the stack frame. va_start initializes a pointer to the start of them and steps down, but it can step down into an unauthorized stack frame.
Generic subroutines and modules
standard subroutines allow the same code to be applied to many different values. Generic subroutines can be applied to many different types.
There is a trade-off involved in balancing the generality of the framework with type safety.