Reputation: 33
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
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
Reputation: 4704
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
Reputation: 28836
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.
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.
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.
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).
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.
A little more about references and pointers can be found in my (very popular) article Addressing pointers.
Upvotes: 6