Search code examples
parameter-passingpass-by-referencepascal

Pass by reference in Pascal


I'm not sure if I understand correctly how pass by reference in Pascal works. Does it create an alias as in C++ (https://isocpp.org/wiki/faq/references) or does it work similarly as in C and the procedure gets a pointer to the variable and uses this pointer.

I guess I could formulate my question as: Does Pascal support true passing by reference, or is it done by call by sharing.

For example FreePascal reference states, that the procedure gets a pointer (https://www.freepascal.org/docs-html/current/ref/refsu65.html), but according to https://swinbrain.ict.swin.edu.au/wiki/Pass_by_Value_vs._Pass_by_Reference#Conclusion and for example https://cgi.csc.liv.ac.uk/~frans/OldLectures/2CS45/paramPassing/paramPassing.html#callByReference pass by reference in Pascal works differently than in C (where pointer is passed).

If anyone can explain a bit more about the differences or how the meaning of pass by reference has changed (in modern languages we say pass by reference, but in fact they are pass by value, like for example Java). What is then the original meaning of pass by reference and how does it work? And how is it then in Pascal?

Thank you very much.


Solution

  • Pass by reference

    in Delphi, pass by reference (using var or out) means passing a pointer. But note that there is a difference between the semantics "pass by value" or "pass by reference" and the actual passing.

    var or out

    The difference with C is not the actual passing (by pointer), just what these keywords mean. Var simply passes by reference. Out treats certain managed types differently, because with out, you tell the compiler the input value is not guaranteed to be initialized. Otherwise, these are technically the same (but not semantically): the address of (IOW, a pointer to) the original values is passed.

    Semantics <> actual passing

    Semantics

    • The semantics of pass by value are that the routine gets a local copy of the original value, and that the routine can (if allowed) change it without affecting the original. The changes apply to the local copy and go away when the routine ends.
    • The semantics of pass by reference are that you are accessing (and perhaps modiyfing) the original.

    Actual low level passing may be different

    But the actually generated code may be different, for reasons of optimization. Especially larger structures (how large depends on version and platform -- this is documented somewhere and has changed over time) are often passed by reference (as pointers) even if the semantics say pass by value. In the prologue of the routine (the hidden code that runs before the first begin) such structures are then copied to the local frame of the routine. Note that this is not done in all calling conventions. Sometimes a full copy is made to the parameter stack before the call and no pointer is passed.

    Since you still have a local copy, and don't modify the original, this is still considered pass by value, just like you declared. All the technical stuff happens transparently for the user, so the difference is only at a lower level. This only matters if you write assembler or must read generated code in the CPU window.

    Const

    If a value parameter is declared const, the original is not modified anyway, so the copying of a large structure to the local frame may be omitted and the values can accessed (read-only) by reference (pointer), even if the semantics are pass by value. Small values are always passed by value (in a register or on the stack), in such cases.

    Although it doesn't make sense to have const var as parameter modifier (something is either constant or variable, but not both), you can still force the passing by reference of a const parameter, by using the [ref] modifier attribute: const [ref] X: Integer or [ref] const X: Integer. The integer would usually be passed by value (in a register or on the stack, depending on calling convention and platform).

    Reference types

    Note that if you pass reference types (e.g. objects, dynamic arrays, etc.) by value, the pass by value semantics only apply to the references themselves, i.e. you get a copy of them and you can modify the reference without affecting the original.

    BUT: the items (objects, arrays, etc.) to which these references point can be modified. Only the references themselves are local copies, not to what they point. So if you call a method on an object reference passed by value, that object (on the heap) may be modified after all! That is not because pass-by-value or pass-as-const do not work properly, but because of what references are.

    Conclusion

    • Pass by value means a copy of the original is made and that copy can be modified locally.
    • Pass by reference means a reference (pointer) to the original is passed and any modification modifies the original.
    • Under the hood, technically, in pass by value semantics, a pointer may be passed after all. If necessary, a copy of the original is made, so the original is not modified after all.
    • Passing reference types by value or even as const does not mean the original (heap) items to which they point are prevented from being modified. There is no C++-like const concept to prevent this.

    A little more about references and pointers can be found in my (very popular) article Addressing pointers.