In some languages such as Pascal, Ada and C++, you can specify that some parameters can be changed inside the subroutine and those changes will "stick" when control returns to the caller. Others, like C and Java, do not permit parameters to be changeable. For example, the following program is legal C but does not do what you expect:
void increment (int x) { // This is legal C x = x + 1; // similar to the x++ operation } int main() { int z = 5; increment(z); printf ("%d", z); // shows 5, not 6 }The reason is that the parameter x in the function increment is a simple int and C does not permit int parameters to be changeable, i.e. to reflect back in their caller any changes to those parameters. We say parameters in C are call by value.
Just to contrast for you, the following C++ program does permit its int parameter to be changeable. Note the additional & symbol after int in the parameter declaration:
void increment (int& x) { // This is legal C++ x = x + 1; // similar to the x++ operation } int main() { int z = 5; increment(z); cout << z; // shows 6 }
cout << z; is the usual way of writing an output statement in C++, though printf can still be used.
C++ arranges for changeable parameters to push changes to them back to the caller. C forces you to do this explicitly through pointers. Though C++ uses pointers, too, it does so behind the scenes.
The next program, containing a short procedure, called augment, illustrates how C does changeable parameters, or rather how it lets you, the programmer, simulate changeable parameters through the use of pointers. augment() has two parameters but no return value because it changes the values of the variables linked to those parameters.
void augment (int *parm1, int parm2) { *parm1 += parm2; } int main() { int k=15, l=20, z=-4, w=6; augment (&k, l); /* after this k is changed to be k+l */ augment (&z, w); /* same idea */ }
Here's the CSC-1 assembler code in 2-column format to save space
0: LOD K 15: PARM1: NUM 0 1: STD PARM1 16: PARM2: NUM 0 2: LOD L 17: K: NUM 15 3: STD PARM2 18: L: NUM 20 4: CAL AUGMENT 19: Z: NUM -4 5: LOD PARM1 20: W: NUM 6 6: STD K 21: AUGMENT: LOD PARM1 7: LOD Z 22: ADD PARM2 8: STD PARM1 23: STD PARM1 9: LOD W 24: RET 10: STD PARM2 11: CAL AUGMENT 12: LOD PARM1 13: STD Z 14: HLT
By the way, this code breaks one of the cardinal rules of assembler programming, which is that comments must exist to explain what is going on in the code.
The way parameters are handled in the assembler subroutine augment is called call by value/result or call by copy-back. It is not exactly equivalent to the C program shown that uses pointers. However, as long as you don't look at the values of the parameters and their referents until the subroutine returns, the two methods appear identical.
If changeable parameters are implemented by pointers, then any changes to the parameters are reflected back to the calling routine's variables immediately. The copy back strategy above is not needed, since the parameters are really just aliases for the calling routine's variables. Let's see what this would look like in assembler.
First the call would look different since we would get the address of the calling routine's variable and store it into the parameter. Using the example augment above,
augment (&k, l); /* after this k is changed to be k+l */
we would do the following:
LDI K STD PARM1 ; K is changeable, so get its address LOD L STD PARM2 ; L is simply call by value, not changeable
Now in the augment subroutine itself, we would see the following:
void augment (int *parm1, int parm2) { int temp = *parm1 + parm2; *parm1 = temp; }
The original code has been expanded and temporaries have been inserted to clarify the steps. To get the value of *parm1, we do the following:
LOD PARM1 ; has the address of the true parameter A2S LDS ; A register now has current value of *parm1 ADD PARM2 STD TEMP
To stash the value back into *parm1, we need to use indirect store:
LOD PARM1 ; has the address of the true parameter A2S LOD TEMP ; what will get stored into *parm1 STS ; go ahead and store, using S's value as address
Putting all these lines together is how we would encode the original statement:
; *parm1 += parm2; LOD PARM1 ; has the address of the true parameter A2S LDS ; A register now has current value of *parm1 ADD PARM2 STD TEMP LOD PARM1 ; has the address of the true parameter A2S LOD TEMP ; what will get stored into *parm1 STS ; go ahead and store, using S's value as address
If you don't have an appreciation for the tremendous work that compilers and high level languages do for you, then you a hopeless dolt!