This laboratory exercise provides initial practice with functions and passing values as parameters within C programs. In many cases, the code you are given or asked to write will cause errors. This is done intentionally!!

HINT: turn statements that are correct into comments rather than deleting them. That makes it easier to restore the program when you are done experimenting.

Consider the program quadratic.c that solves the quadratic formula ax2 + bx + c = 0, while illustrating several common variations for simple functions.

void and non-void Functions

Read the directions carefully; sometimes you are asked to predict what will go wrong and then test it. Sometimes, you are encouraged to fix it. Use comments to temporarily change code rather than deleting.

  1. Copy quadratic.c to your account, compile and run it, and review the code to determine how it works.

    1. Procedure printEquation has a void return type. Parameters are passed into printEquation, but nothing is passed back. What will happen if the statement
      return coeff2 + coeff1 + coeff0;
      is inserted at the end of this procedure?
    2. Verify your prediction by making the modification and compiling the code. Explain the result.
    3. Restore the program to its original state.
    4. Procedure printEquation is called in both procedures displayExample1 and displayExample2 with just a simple statement, such as:
      printEquation (1.0, -3.0, 2.0);
      Predict what will happen if you try to assign the result of printEquation to a variable, such as
      double value = printEquation(1.0, -3.0, 2.0);
      After making your prediction, verify whether you were correct by making the change and compiling the code. Explain the results.
    5. Restore the program again to its original state.
    6. Procedure computeDiscriminant returns a double. Sometimes beginning coders separate the call to a function with the assignment of the return value. Here are three examples:

      Attempt 1:

      double discriminant;
      computeDiscriminant (a, b, c);

      Attempt 2:

      computeDiscriminant (a, b, c);
      double discriminant;

      Try each of these attempts within procedure printRoots.

      • Does the code compile?
      • Does the code give the desired result?
      When you are finished, restore the program to its original state.
    7. Change the body of computeDiscriminant to the following
      printf ("r = %lf, s = %lf, t = %lf\n", r, s, t);
      return sqrt (s * s - 4 * r * t);
      Compile and run the program.
      • Describe what is printed by the overall program. What might you conclude about what happens when printing is done in the middle of a computation? For example, how does this impact the overall program's output?
      • Can you identify any guidelines regarding the advisability of including print statements in functions designed for computation (e.g., computeDiscriminant)?
      • In addition to the printf statement in function computeDiscriminant, insert the Attempt 1 code from Step 1.6 (into printRoots). What happens when procedure with a return value (e.g., computeDiscriminant) is called on a line by itself?
      Restore the program to its original state.

Writing Numeric Functions

  1. Within a C program, define and use the following functions:
    • Function circum that takes a circle's radius as parameter and returns the circumferences of the circle.
    • Function area that takes a radius as parameter and returns the area of the circle with that radius.

The Perils of Global Variables

Global variables are variables declared outside any particular function. In terms of our stack model of computation, they exist in a separate part of memory called the static region, which you can draw in your diagrams as a collection of variable-value pairs separate from the stack. They tend to be problematic and are generally to be avoided. Instead, you should favor explicitly passing data between function calls using parameters and return values.

  1. To better understand the confusions global variables can cause, consider the following program.
    #include <stdio.h>
    int glob = 10;
    g (void) {
        // Point (B)---first call to g()
        for (int i = 0; i < glob; i++) {
            printf("%d ", i);
        glob += 1;
        // Point (C)---final call to g()
    } // g
    f (void) {
        for (int i = 0; i < glob; i += 2) {
            // Point (A)---first iteration of the for-loop
        return glob;
    } // f
    main (void) {
        int ret = f();
        // Point (D)
    } // main
    1. Predict what the program will print.
    2. Verify your prediction by compiling and running the code. If your prediction was incorrect, trace the execution of the program by recording and updating the stack (including the static region) diagram as you trace the code's execution.

Functions, Values as Parameters, and Robot Motion

Program yoyo.c uses the function yoyo to control a Scribbler 2 robot.

  1. Copy yoyo.c to your account, compile and run it, and review to code to determine how it works.
    1. Explain what the program does.
      • What movements does the robot make? Why?
      • What output is printed? Why?
    2. In the main program, duplicate the line
      result = yoyo (repetitions);

      (I.e., this line should appear twice in succession.)

      Does making this call twice change how many times the robot yoyos in each call? Why or why not?

    3. In the main program, replace the line
      result = yoyo (repetitions);
      repetitions = yoyo (repetitions);
      repetitions = yoyo (repetitions);
      Does making this call twice change how many times the robot yoyos in each call? Why or why not?
    4. In C (as in Scheme), variables declared within a function are separate from variables declared elsewhere (e.g., in main). In the original program, change the name of the repetitions variable to reps throughout the main procedure. Compile and run the program.

      Does changing yoyo's variable reps have any impact on the variable reps in the main procedure?

    5. Again, return to the original program. This time nest the call to yoyo in the main procedure. That is, the call to yoyo will become:
      yoyo (yoyo (repetitions));
      • Make a prediction of what will happen with the above nested call.
      • Test out your nested call and explain what happened.
  2. Recall the reading's discussion of memory diagrams. Draw a sequence of stack diagrams for program quadratic.c from Step 1.

    Important: To benefit from this exercise, you must actually draw these diagrams. It is not a mental exercise.

  3. Consider the program value-params.c.
    1. Copy the program to your account, compile, and run it.
    2. Draw stack diagrams for the program state when each print statement is executed. Be sure you can explain the running of the program at each step, based upon this series of diagrams.
Exercise 3 is a derivative work of Parameters and Mental Models, by Titus Klinge and Peter-Michael Osera, used under a Creative Commons Attribution-NonCommercial 4.0 license.