Worksheet Argument Passing

Table of Contents

1. Questions and Completion

If you have questions as you go through this worksheet, please feel free to post them on the discussion forum.

2. Finding Current Information

The Concepts textbook is helpful in providing some information for topics this week. However, implementation details differ significantly between PLs, so it is necessary to supplement textbook reading with experiments and more current information online (often blog posts of experiments by other people). Unfortunately, book length treatments of implementation details are becoming rarer as their shelf-life decreases.

3. L-Value or Not?

In each of the following contexts, determine whether the given expression is an l-value or not. Your code should replace the ellipsis (...).

  1. Is x++ an l-value in C? The context is:

    void f (int x) {
      ...
    }
    
  2. Is arr[x++] an l-value in C? The context is:

    void f (int x) {
      int arr[] = { 5, 6, 7, 8, 9, 10, 11 };
      ...
    }
    
  3. Is f ().x an l-value in C? The context is:

    struct S {
      int x;
      int y;
    };
    
    struct S f () {
      struct S s;
      s.x = 5;
      s.y = 6;
      return s;  // returns a copy of the "struct S", i.e., copies the two int members back
    }
    
    void g () {
      ...
    } 
    
  4. Is t.x an l-value in C? The context is:

    struct S {
      int x;
      int y;
    };
    
    struct S f () {
      struct S s;
      s.x = 5;
      s.y = 6;
      return s;  // returns a copy of the "struct S", i.e., copies the two int members back
    }
    
    void g () {
      struct S t = f ();
      ...
    } 
    
  5. Is f ()->x an l-value in C? Recall that this means (*(f())).x, i.e., call f, dereference the pointer, then access the x member of the struct. The context is:

    #include <stdlib.h>
    
    struct S {
      int x;
      int y;
    };
    
    struct S *f () {
      struct S *p = (struct S *) malloc (sizeof (struct S));
      p->x = 5;   // recall that p->x is just shorthand for (*p).x
      p->y = 6;
      return p;  // returns a copy of the pointer, i.e., copies just a pointer back
    }
    
    void g () {
      ...
    } 
    
  6. Is arr[x++] an l-value in Java? The context is:

    class LValue6 {
      static void f (int x) {
        int[] arr = { 5, 6, 7, 8, 9, 10, 11 };
        ...
      }
    }
    
  7. Is list.get (x++) an l-value in Java? The context is:

    import java.util.ArrayList;
    
    class LValue7 {
      static void f (int x) {
        ArrayList<Integer> list = new ArrayList<> (); 
        list.add (5); 
        list.add (6); 
        list.add (7); 
        list.add (8); 
        ...
      }
    }
    
  8. Is list.get (x++).x an l-value in Java? The context is:

    import java.util.ArrayList;
    
    class C {
      int x;
      int y;
      C (int x, int y) {
        this.x = x;
        this.y = y;
      }
    }
    
    class LValue8 {
      static void f (int x) {
        ArrayList<C> list = new ArrayList<> (); 
        list.add (new C (5, 5)); 
        list.add (new C (6, 6)); 
        list.add (new C (7, 7)); 
        list.add (new C (8, 8)); 
        ...
      }
    }
    

Solution: L-Value or Not?

4. Argument Passing

4.1. CBV in C - Int

Confirm that C uses call-by-value to pass int variables by writing a simple C program then running it.

4.2. CBV in C - Pointer

Confirm that C uses call-by-value to pass pointer variables by writing a simple C program then running it.

Solution: CBV in C?

4.3. CBV in Java - Int

Confirm that Java uses call-by-value to pass int variables by writing a simple Java program then running it.

4.4. CBV in Java - Reference

Confirm that Java uses call-by-value to pass reference variables (meaning references to instances on the heap) by writing a simple Java program then running it.

Solution: CBV in Java?

4.5. Scala - Reference

When the following Scala program is executed it prints C(10). Can we conclude that Scala uses call-by-reference?

case class C (var x:Int)

object Test {
  def f (o:C) = {
    o.x = 10
  }

  @main def main () = {
    val c = C (5)
    f (c)
    println (c)
  }
}

4.6. C - Struct

Examine the following C program. A struct S contains two int members (like fields) and an array of int (length is ARR_SIZE or 16).

#include <stdio.h>
#include <stdlib.h>

#define ARR_SIZE 16

struct S {
  int x;
  int y;
  int arr[ARR_SIZE];
};

void print_S (struct S *p) {
  printf ("S {\n  x = %d,\n  y = %d,\n  arr = [ ", p->x, p->y);
  for (int i = 0; i < ARR_SIZE; i++) {
    printf ("%02X, ", p->arr[i]);
  }
  printf ("],\n}\n");
}

void f (struct S s) {
  printf ("s in f #1\n");
  print_S (&s);
  s.x = s.x + 1;                /* increment s.x      (first way to write it) */
  s.y += 1;                     /* increment s.y      (second way to write it) */
  for (int i = 0; i < ARR_SIZE; i++) {
    s.arr[i]++;                 /* increment s.arr[i] (third way to write it) */
  }
  printf ("s in f #2\n");
  print_S (&s);
}

int main (void) {
  struct S t;
  t.x = 5;
  t.y = 6;
  for (int i = 0; i < ARR_SIZE; i++) {
    t.arr[i] = i;
  }
  //printf ("sizeof(struct S)=%lu\n", sizeof(struct S));
  printf ("t in main #1\n");
  print_S (&t);
  f (t);
  printf ("t in main #2\n");
  print_S (&t);
}
  1. How big is a struct S? (You can check your answer afterwards by adding a line to print sizeof(struct S).)
  2. In contrast how big is each field in an instance of this Java class?

    class S {
      int x;
      int y;
      int[] arr;
    } 
    
  3. The main function allocates a variable t of type struct S in its activation on the call-stack. We often say that t is stack-allocated. The function f is called with t as an argument. The function f prints the contents of its argument, updates its argument, then prints the contents again. Finally main prints the contents of t again. Try to work out will be printed, given the statement "C copies structures when passed as arguments". Confirm your answer by compiling and running the C program.
  4. Rewrite the C program so that f receives a pointer to a struct S, and does its updates via that pointer. Try to work out how that will affect the output. Recompile and run your program to confirm your answer.

5. Returning Two Items

It is often necessary to return two or more items from a function or method.

In Scala, tuple types can be used to return two items. For example, fibaux below performs an efficient calculation of the n and n+1 elements of the Fibonacci sequence. By returning a pair of two consecutive numbers from the Fibonacci sequence it avoids the unnecessary recomputation in the naive definition of the Fibonacci function.

def fibaux (n:BigInt) : (BigInt, BigInt) = {
  if (n <= 0) {
    (0, 1)
  } else if (n == 1) {
    (1, 1)
  } else {
    val (a, b) = fibaux (n - 1)
    (b, a + b)
  }
}

def fib (n:BigInt) : BigInt = fibaux (n)._1

For example:

scala> (0 to 70).toList.map (n => BigInt (n)).map (fib)
res0: List[BigInt] = List(0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437, 701408733, 1134903170, 1836311903, 2971215073, 4807526976, 7778742049, 12586269025, 20365011074, 32951280099, 53316291173, 86267571272, 139583862445, 225851433717, 365435296162, 591286729879, 956722026041, 1548008755920, 2504730781961, 4052739537881, 6557470319842, 10610209857723, 17167680177565, 27777890035288, 44945570212853, 72723460248141, 117669030460994, 190392490709135)

Now consider how to return two or more items in other programming languages.

5.1. Java - Immutable Pair Class

The following Java program defines an immutable Pair class (with type parameters X and Y). Complete the definition of fibaux in the following Java program, which uses the immutable Pair class. You may need to refer to the Java documentation for use of Java's BigInteger class.

import java.math.BigInteger;

class Pair<X,Y> {
  final X x;
  final Y y;
  Pair (X x, Y y) {
    this.x = x;
    this.y = y;
  }
}   

public class Fib1 {
  static Pair<BigInteger,BigInteger> fibaux (BigInteger n) {
    ...
  }

  static BigInteger fib (BigInteger n) {
    return fibaux (n).x;
  }

  public static void main (String[] args) {
    for (int i = 0; i < 71; i++) {
      System.out.println (fib (BigInteger.valueOf (i)));
    }
  }
}

Solution: Returning Two Items - Java - Immutable Pair Class

5.2. Java - Mutable Pair Class

If the final modifier is removed from the fields of Pair, the caller to fibaux can pass in (a reference to) a Pair instance, and the fields of that instance can be updated with the results. Complete the definition of fibaux in the following Java program.

import java.math.BigInteger;

class Pair<X,Y> {
  X x;
  Y y;
  Pair (X x, Y y) {
    this.x = x;
    this.y = y;
  }
}   

public class Fib2 {
  static void fibaux (BigInteger n, Pair<BigInteger,BigInteger> result) {
    ...
  }

  static BigInteger fib (BigInteger n) {
    Pair<BigInteger, BigInteger> p = new Pair<> (BigInteger.ZERO, BigInteger.ZERO);
    fibaux (n, p);
    return p.x;
  }

  public static void main (String[] args) {
    for (int i = 0; i < 71; i++) {
      System.out.println (fib (BigInteger.valueOf (i)));
    }
  }
}

Solution: Returning Two Items - Java - Mutable Pair Class

5.3. C - Immutable Struct

The following C program defines an immutable pair struct. We use long for simplicity instead of an arbitrary precision data type for integers. Complete the definition of fibaux in the following C program, which uses the immutable pair struct.

#include <stdio.h>
#include <stdlib.h>

struct pair {
  const long x;
  const long y;
};

struct pair fibaux (long n) {
  ...
}

long fib (long n) {
  return fibaux (n).x;
}

int main (void) {
  for (int i = 0; i < 71; i++) {
    printf ("%ld\n", fib (i));
  }
}

Solution: Returning Two Items - C - Immutable Pair Struct

5.4. C - Mutable Pair Struct

Explain why we cannot remove the const modifier from the members of struct pair and then rewrite fibaux with the following signature (contrast with the Java mutable Pair class above).

struct pair {
  long x;
  long y;
};

void fibaux (long n, struct pair result) {
  ...
}

Explain how to change the signature of fibaux so that an argument for the result can be passed to fibaux.

Solution: Returning Two Items - C - Mutable Pair Struct

6. Scope and Recursive Values

  1. Write a Scala value alternates of type LazyList[Boolean] that alternates between true and false (starting with true).
  2. Try the following expressions in the Scala REPL (after your definition of alternates).

    "the rain in spain".toList
    
    "the rain in spain".toList.zip (alternates)
    
    "the rain in Spain".toList.zip (alternates).filter ((p:(Char,Boolean)) => p match { case (c:Char,b:Boolean) => b })
    
    /* shorthand for the previous version */
    "the rain in Spain".toList.zip (alternates).filter { case (c:Char,b:Boolean) => b }     
    
    "the rain in Spain".toList.zip (alternates).filter { case (c:Char,b:Boolean) => b }     
    
    val evens = "the rain in Spain".toList.zip (alternates).filter { case (c:Char,b:Boolean) => b }.map (p => p._1)
    
    val odds = "the rain in Spain".toList.zip (alternates).filter { case (c:Char,b:Boolean) => !b }.map (p => p._1)
    
    evens.zip (odds)
    

7. Solutions

7.1. Solution: L-Value or Not?

  1. No, temporary value from ++
  2. Yes, array
  3. No, temporary value from f()
  4. Yes, struct
  5. Yes, pointer is temporary, but field is not
  6. Yes, array
  7. No, temporary Integer
  8. Yes, all object access via pointers/references in Java

7.2. Solution: CBV in C

#include <stdio.h>
void swapInt(int x, int y) {
    int oldX = x;
    int oldY = y;
    x = oldY;
    y = oldX;
}
void swapPointer(int *x, int *y) {
    int* oldX = x;
    int* oldY = y;
    x = oldY;
    y = oldX;
}
int main() {
    {
        int a = 0;
        int b = 1;
        printf ("a = %d, b = %d\n", a, b);
        swapInt (a, b);
        printf ("a = %d, b = %d\n", a, b);
    }
    {
        int a = 0; int *p = &a;
        int b = 1; int *q = &b;
        printf ("*p = %d, *q = %d\n", *p, *q);
        swapPointer (p, q);
        printf ("*p = %d, *q = %d\n", *p, *q);
    }
}

7.3. Solution: CBV in Java

class CBVBase {   
    private static void swapBase(int x, int y) {
        int oldX = x;
        int oldY = y;
        x = oldY;
        y = oldX;
    }
    public static void main(String args[]) {
        int a = 0;
        int b = 1;
        System.out.format ("a = %d, b = %d\n", a, b);
        swapBase (a, b);
        System.out.format ("a = %d, b = %d\n", a, b);
    }
}
class CBVRef {   
    private static void swapRef(Integer x, Integer y) {
        Integer oldX = x;
        Integer oldY = y;
        x = oldY;
        y = oldX;
    }
    public static void main(String args[]) {
        Integer a = 0;
        Integer b = 1;
        System.out.format ("a = %d, b = %d\n", a, b);
        swapRef (a, b);
        System.out.format ("a = %d, b = %d\n", a, b);
    }
}

7.4. Solution: Returning Two Items - Java - Immutable Pair Class

import java.math.BigInteger;

class Pair<X,Y> {
  final X x;
  final Y y;
  Pair (X x, Y y) {
    this.x = x;
    this.y = y;
  }
}   

public class Fib1 {
  static Pair<BigInteger,BigInteger> fibaux (BigInteger n) {
    if (n.compareTo (BigInteger.ZERO) <= 0) {
      return new Pair<> (BigInteger.ZERO, BigInteger.ONE);
    } else if (n.equals (BigInteger.ONE)) {
      return new Pair<> (BigInteger.ONE, BigInteger.ONE);
    } else {
      Pair<BigInteger, BigInteger> p = fibaux (n.subtract (BigInteger.ONE));
      return new Pair<> (p.y, p.x.add (p.y));
    }
  }

  static BigInteger fib (BigInteger n) {
    return fibaux (n).x;
  }

  public static void main (String[] args) {
    for (int i = 0; i < 71; i++) {
      System.out.println (fib (BigInteger.valueOf (i)));
    }
  }
}
$ javac Fib1.java 

$ java Fib1
0
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946
17711
28657
46368
75025
121393
196418
317811
514229
832040
1346269
2178309
3524578
5702887
9227465
14930352
24157817
39088169
63245986
102334155
165580141
267914296
433494437
701408733
1134903170
1836311903
2971215073
4807526976
7778742049
12586269025
20365011074
32951280099
53316291173
86267571272
139583862445
225851433717
365435296162
591286729879
956722026041
1548008755920
2504730781961
4052739537881
6557470319842
10610209857723
17167680177565
27777890035288
44945570212853
72723460248141
117669030460994
190392490709135

7.5. Solution: Returning Two Items - Java - Mutable Pair Class

import java.math.BigInteger;

class Pair<X,Y> {
  X x;
  Y y;
  Pair (X x, Y y) {
    this.x = x;
    this.y = y;
  }
}   

public class Fib2 {
  static void fibaux (BigInteger n, Pair<BigInteger,BigInteger> result) {
    if (n.compareTo (BigInteger.ZERO) <= 0) {
      result.x = BigInteger.ZERO;
      result.y = BigInteger.ONE;
    } else if (n.equals (BigInteger.ONE)) {
      result.x = BigInteger.ONE;
      result.y = BigInteger.ONE;
    } else {
      fibaux (n.subtract (BigInteger.ONE), result);
      BigInteger x = result.x;
      BigInteger y = result.y;
      result.x = y;
      result.y = x.add (y);
    }
  }

  static BigInteger fib (BigInteger n) {
    Pair<BigInteger, BigInteger> p = new Pair<> (BigInteger.ZERO, BigInteger.ZERO);
    fibaux (n, p);
    return p.x;
  }

  public static void main (String[] args) {
    for (int i = 0; i < 71; i++) {
      System.out.println (fib (BigInteger.valueOf (i)));
    }
  }
}

7.6. Solution: Returning Two Items - C - Immutable Pair Struct

#include <stdio.h>
#include <stdlib.h>

struct pair {
  const long x;
  const long y;
};

struct pair fibaux (long n) {
  if (n <= 0) {
    return (struct pair) { 0, 1 };
  } else if (n == 1) {
    return (struct pair) { 1, 1 };
  } else {
    struct pair p = fibaux (n - 1);
    return (struct pair) { p.y, p.x + p.y };
  }
}

long fib (long n) {
  return fibaux (n).x;
}

int main (void) {
  for (int i = 0; i < 71; i++) {
    printf ("%ld\n", fib (i));
  }
}

7.7. Solution: Returning Two Items - C - Mutable Pair Struct

It would not work because fibaux would receive a copy of the struct pair from the caller, so modifications to result would not be seen by the caller.

To modify the program, we pass a pointer to a struct pair instead, and fibaux fills in its value.

#include <stdio.h>
#include <stdlib.h>

struct pair {
  long x;
  long y;
};

void fibaux (long n, struct pair *presult) {
  if (n <= 0) {
    presult->x = 0; 
    presult->y = 1; 
  } else if (n == 1) {
    presult->x = 1; 
    presult->y = 1; 
  } else {
    fibaux (n - 1, presult);
    long x = presult->x;
    long y = presult->y;
    presult->x = y; 
    presult->y = x + y; 
  }
}

long fib (long n) {
  struct pair result; // not initialized here, will be initialized by fibaux
  fibaux (n, &result);
  return result.x;
}

int main (void) {
  for (int i = 0; i < 71; i++) {
    printf ("%ld\n", fib (i));
  }
}

7.8. Solution: Scope and Recursive Values

With safe initialization enabled in Scala 3.0, you need to write it this way:

val alternates = 
  def generator():LazyList[Boolean] = true #:: false #:: generator() 
  generator()

With safe initialization off, you can write simply:

val alternates = LazyList[Boolean] = true #:: false #:: alternates

(It appears that the safe initialization checker does not know that #:: is non-strict.)

Author: James Riely

Created: 2024-04-17 Wed 17:42

Validate