pjSonic
pjSonic

Reputation: 33

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.

Upvotes: 3

Views: 6279

Answers (3)

Sam Ginrich
Sam Ginrich

Reputation: 851

The term "reference" was invented in the early years of programming languages, in Algol, Fortran or Pascal.

A reference is a sharing alias. The essential technical property of a reference is the address of the shared instance. In C++ a reference is equivalent to a de-referenced pointer.

// C++
int z = 29; // instance
int& alias = z; // reference
int* p = &alias; // pointer, address of z
int& r = *p; // reference

In Pascal references only exists as var-parameters of a procedures or functions or as de-referenced pointers

// PASCAL
z: INTEGER = 29; // instance
p: ^INTEGER = @z; // pointer, address of z
// p^ is a reference


PROCEDURE RefDemo(var x: integer)
VAR q: ^INTEGER;
BEGIN
   q =@x;
   RefDemo(q^);
END;

This var x: INTEGER exists equivalent in C++

void RefDemo(short int& x)
{
    short int* q = &x;
    RefDemo(*q);
}

In retrospective such reference p^exists in C, too, as *p. Though the inventors did not use this naming. They even decided to call a pointer a value, rather than the referenced instance (-value).

Now doing the same with objects

// Pascal
type 
   Books = OBJECT
      title: packed array [1..50] of char;
      author: packed array [1..50] of char;
      subject: packed array [1..100] of char;
      book_id: integer;
   end;
...

PROCEDURE RefDemo(var book: Books)
VAR q: ^Books;
    otherBook: Books;
BEGIN
   book.author[1] := '-';
   book := otherBook; // assigns all attributes
   RefDemo(q^);
END;

// C++
class Books
{
public:
   char title[50];
   char author[50];
   char subject[50];
   short int book_id;
}
...

void RefDemo(Books& book)
   Books *q;
   Books otherBook;
{
   q = &book;
   book.author[0] = '-';
   book = otherBook; // assigns all attributes [1]      
   RefDemo(*q);
}

// C emulation
typedef struct _Books
{
   char title[50];
   char author[50];
   char subject[50];
   short int book_id;
} Books;
...

void RefDemo(Books* book)
   Books *q;
   Books otherBook;
{
   q = book;
   book->author[0] = '-';
   *book = otherBook; // assigns all attributes [1]       
   RefDemo(*q);
}

Off Topic, but for completing comments:

In Java Object parameters behave precisely like the "By-Reference"-emulation in C, except we have no de-referentiation operator and no built-in assignment operator for complete objects instances as in [1].

Designers of Java followed ideas of C and breaking with there own paradigm of Java being object oriented language, they consider object references as values rather than object instances and this is the origin of endless discussions on calling-conventions.

We'll never get to know, what a Niklaus Wirth would say about an even grammatically weak statement like "Java is pass by value?".

Upvotes: 1

A simpler answer than the (very good) other.

does it work similarly as in C and the procedure gets a pointer to the variable and uses this pointer.

Yes, that's what happens under the hood. The procedure gets really a pointer, an address, to the variable. But the compiler, knowing that, makes it transparent. So, inside a procedure which declares a parameter "a", the statement:

a := a div 2;

can be compiled in two different ways. If the a parameter is declared normally, i.e. passed by value, the statement is compiled like:

1 - load the value at address "a"
2 - integer divide by two
3 - store the result at address "a"

If, instead, the parameter was declared as var, meaning passed by reference, the compiler does:

1 - load the value at address "a"
2 - load the value at address just loaded (it's a pointer dereferencing)
3 - divide
4 - store back

The above four statements are exactly what C compiles if the source is:

*a = *a / 2;

I guess I could formulate my question as: Does Pascal support true passing by reference ...?

The answer is absolutely yes, true passing by reference, and not many languages do this so well and cleanly. The source code for calling a procedure does not change, whether the procedure is called with "by reference" or "by value". Again the compiler, knowing how the formal parameters are to be passed, hides the details, unlike C. For example, take a call to a procedure which wants a parameter passed by value:

myproc(a);  // pascal and C are identical

Things get different if the procedure expect a pass by reference:

myproc(a);  // pascal: identical to before

myproc(&a); // C: the language does not hide anything

About this last point, someone thinks that C is best because it forces the programmer to know that the passed variable can be modified by the procedure (function). I think, instead, that pascal is more elegant, and that a programmer should know anyway what the procedure will do.

All this is for "simple" types. If we talk of strings (and modern pascal has two flavours of them), it's the same - in Pascal. The compiler copies, increments reference counts, does whatever is needed to have full support for passing by value or by reference. C language does not have something similar.

If we talk about classes, things are different, but they must differ anyway because of the semantics of classes.

I hope to have added something to the other complete answer.

Upvotes: 3

Rudy Velthuis
Rudy Velthuis

Reputation: 28836

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.

Upvotes: 6

Related Questions