Parameter Passing

From Rosetta Code

Parameters of a subprogram refer to its arguments and results. A parameter can be passed in one of two modes:

  • by value (or else by copy)
  • by reference

When the parameter is passed by value, within the subprogram the object denoted by the formal parameter (the identifier of the parameter) is distinct from the object of the actual parameter (the value that has been passed in). In particular, the formal parameter has an independent set of states, so that if the object is mutable, then updates to the object do not influence the state of the actual parameter object, so long as the program has not left the subprogram.

When the parameter is passed by reference, the formal object represents an aliased view of the actual object. Thus, updates of the formal object are instantly reflected by the actual object.

The mutability of the parameter is independent on the parameter passing mode. In general the formal parameter can have any of three access modes:

  • in (immutable)
  • out (update only, usually used for the results)
  • in-out (mutable)

For example, when an in-out parameter is passed by value, the compiler creates a copy of it and makes the copy available to the subprogram. After the subprogram completion, the value of the copy is written back to the actual object.

The language's choices of parameter passing modes may vary greatly, as the following examples show.

Example 6502 Assembly

As is typical of assembly languages, 6502 Assembly doesn't restrict how parameters are passed to functions in any way. Read-only data is of course immutable, but this is just the nature of read-only memory and not a limitation imposed by the language.

Opcodes

Certain commands such as INC,DEC,ASL,LSR,ROL, and ROR inherently alter the memory address that is used as an argument. Thus their use on a memory address is pass-by-reference.

<lang 6502asm>ROL $81 ;rotate left the bits of the value stored at memory address $0081. The old value of $0081 is discarded. INC $20 ;add 1 to the value at memory address $20.</lang>

However, opcodes that use a register as their primary operand are strictly pass-by-value.

<lang 6502asm>ADC $81 ;add the value stored at memory address $81 to the accumulator. The value at memory address $81 is unchanged.

         ;If the carry was set prior to this operation, also add an additional 1 to the accumulator.

LDX $20 ;load the value at memory address $20 into the X register. DEX ;subtract 1 from the value in the X register. This is only a copy. The actual value stored at $20 is unchanged.

BIT $2002 ;Set the flags as if the value stored at address $2002 was bitwise ANDed with the accumulator.

         ;Neither the accumulator nor the value stored at $2002 is changed.

LDA $40 ;load the value stored at memory address $40 into the accumulator ROR A ;rotate right the accumulator. This is only a copy. The actual value stored at $40 is unchanged.</lang>

Functions

Whether a function's parameters are pass-by-value or pass-by-reference ultimately depends on how it is written and which opcodes are used. Generally, parameters are either passed using the stack or with temporary zero-page memory addresses. While these are both forms of memory that are getting overwritten, the actual sources of the data remain the same.

Example of in (immutable)

<lang 6502asm> lda sourceData

   jsr MultiplyBy4


MultiplyBy4: ; pretend this routine is somewhere far away.

   lsr A
   lsr A
   rts</lang>
Example of out (result)

This routine reads the joystick output on Commodore 64 and stores it in the output variable joystick1. <lang 6502asm>ReadJoystick:

   lda $DC00      
   ora #%11100000
   sta joystick1
   rts</lang>
Example of in/out (mutable)

This one's tricky because of the way pointers work on the 6502. Unlike the other architectures of its day there are no "address registers" per se, but we can make our own using the zero page.

<lang 6502asm> LDA #$03 ;load the low byte of the parameter's address

   STA $20  ;store it in the low byte of our pointer variable.
   LDA #$00 ;load the high byte.
   STA $21  ;store it in the high byte of our pointer variable. 
   ;we have to do it this way because a simple "LDA $03" cannot remember the source address after it is executed.
   ;now we can LDA ($20),y and STA ($20),y to load and store to/from the same place, regardless of where that place actually is.
   LDY #0          ;6502 requires Y to get involved, set it to 0 to have no offset.
   jsr Add32


Add32:  ; pretend this subroutine is somewhere far away.

   LDA ($20),y     ; the input parameter is actually address $0003
   clc
   adc #$20        ; 0x20 = decimal 32
   sta ($20),y     ; overwrite the old value stored in $0003 with the new one.
   rts</lang>

Example Ada

Ada uses both by value and by reference passing and all three parameter access modes:

  • objects of the scalar types are passed by value
  • objects with an identity are passed by reference. Objects with identities are task types, protected types, non-copyable (limited) types, tagged types (OO objects with type identity)
  • for all other types the choice is left to the compiler.

Example C/C++

C and C++ use only by value parameter passing. Arguments are mutable as if they were in-out, but the compiler does not store the copies back. Results also passed by copy (copy out). Where by-reference passing is needed, the C/C++ deploys referential types T* and T& (in C++). So when an object of the type T needs to be passed by reference, another object of either the type T* or T& is passed by value instead. Because objects in C++ can be automatically converted to references, in many cases the difference is not noticeable.

Example Fortran

Early versions of the language used only by reference parameter passing for the arguments.

Starting from Fortran 90 (and later), functions and subroutines can specify an intent for the argument; intent can be: in (unmodificable argument), out (argument used to return a value; the starting value must be intended not significative, undefined), inout (argument both is intended for input and can be modified, i.e. return the value modified by the function/subroutine).

Trying to modify an argument with intent in triggers an error at compile time.