9.1 Procedures, Parameters, Return Values, Nested Calls, and Scope

Key Takeaways

  • A procedure header such as int newScore ( int a, int b, int old ) gives the return type, the name, and the typed parameters; void means nothing is returned.
  • Arguments are matched to parameters by position, and a return statement ends the procedure immediately and sends a value back to the caller.
  • In nested calls such as twice ( plusThree ( 4 ) ), the innermost call runs first and its result becomes the argument of the outer call.
  • Assigning a new value to a primitive parameter inside a procedure does not change the caller's variable, but changing an element of an array parameter changes the caller's array.
  • A local variable exists only inside its procedure or block; a local variable with the same name as a global variable shadows it without changing it.
Last updated: September 2026

What this competency asks

ETS asks you to understand how to write and call procedures with parameters and return values. Besides tracing, finding missing code, and identifying equivalent code, two skills are specific to procedures:

  • Trace code when references to objects and arrays are passed to procedures.
  • Trace code that includes nested procedure calls.

The data-types competency adds: distinguish between global and local scope.

Why procedures matter

A procedure (also called a function, method, or subroutine) packages a task behind a name. This gives you procedural abstraction: callers need to know what the procedure does, not how. Procedures avoid duplicated code, let a large program be built and tested in pieces, and make changes safer, because a fix in one place applies everywhere the procedure is called. ETS's sample dice-game question hinges on choosing the correct body for a procedure newScore ( dieOne, dieTwo, score ) whose purpose is described only by a table of results.

Procedure syntax in ETS pseudocode

int area ( int width, int height )        // returns an int
    return width * height
end area

void printBanner ( String title )         // returns nothing
    print "*** " + title + " ***"
end printBanner
TermMeaningExample
Parameter (formal parameter)A variable named in the headerwidth, height
Argument (actual parameter)The value supplied in the callarea ( 3, 4 ): 3 and 4
Return typeType of the value sent backint, or void for none
Return valueThe value produced by return12

Arguments match parameters by position: the first argument goes to the first parameter, and so on. A return statement ends the procedure at once. Statements after it in the same path never run.

A value-returning procedure is used inside an expression: int a ← area ( 3, 4 ) + 1. A void procedure is called as a statement on its own: printBanner ( "Welcome" ).

Tracing a call

For each call:

  1. Evaluate the arguments.
  2. Create fresh parameter variables and copy the argument values into them.
  3. Run the body, tracking local variables.
  4. At return, send the value back to the exact place the call appeared.
int addBonus ( int score )
    score ← score + 5
    return score
end addBonus

int s ← 80
int t ← addBonus ( s )
print s + " " + t

The parameter score starts as a copy of s (80) and becomes 85, which is returned into t. The caller's s is unchanged, so the output is 80 85.

Nested calls

When a call appears as an argument, evaluate from the inside out:

int twice ( int x )
    return 2 * x
end twice

int plusThree ( int x )
    return x + 3
end plusThree

print twice ( plusThree ( 4 ) ) + plusThree ( twice ( 4 ) )
  • plusThree ( 4 ) = 7, so twice ( 7 ) = 14.
  • twice ( 4 ) = 8, so plusThree ( 8 ) = 11.
  • The output is 14 + 11 = 25. The order of composition matters.

Procedures can also call other procedures from inside their bodies. Keep a stack of "who is waiting for whom". Each caller pauses until its callee returns, exactly as in recursion (Section 6.3).

Passing arrays and objects

A variable of a primitive type (int, double, boolean, char) holds its value directly. A variable that refers to an array or object holds a reference to it. When you pass it, the procedure receives a copy of the reference, so both names refer to the same array.

void update ( int[ ] arr, int x )
    arr[0] ← arr[0] + 10     // changes the caller's array
    x ← x + 10               // changes only the local copy
end update

int[ ] nums ← {1, 2, 3}
int y ← 5
update ( nums, y )
print nums[0] + " " + y

The output is 11 5. The array element changed because arr and nums refer to the same array. y did not change because x was a separate copy.

A subtler case, following the Java model: if a procedure assigns a whole new array to its parameter (arr ← new array), only the local reference changes, and the caller still refers to the original array. ETS's sample sort question relies on this sharing: a helper call swap ( arr, i, j ) exchanges two elements of the caller's array.

Some languages, such as C++ with & parameters, also offer pass-by-reference for primitive variables, which lets the procedure change the caller's variable directly. Unless a question says so, assume primitives are copied.

Scope and lifetime

Scope is the region of code where a name can be used. Lifetime is how long the variable exists while the program runs.

KindDeclaredVisibleExists
LocalInside a procedure or block (including loop headers)Only thereWhile that procedure call or block runs
ParameterIn the procedure headerOnly inside the procedureFor one call
GlobalOutside all proceduresEverywhere, unless shadowedFor the whole program

Shadowing: a local variable with the same name as a global variable hides the global inside that procedure.

int count ← 100                 // global

int process ( )
    int count ← 10              // local; shadows the global
    for ( int k ← 1; k ≤ 3; k ← k + 1 )
        count ← count + k
    end for
    return count
end process

int result ← process ( )
print count + " " + result

The local count becomes 10 + 1 + 2 + 3 = 16 and is returned. The global count is untouched. Output: 100 16. The declaration int count inside the procedure is what creates the new local variable. Without it, the procedure would be updating the global.

Why avoid globals? Any procedure can change a global variable, which creates hidden dependencies. A bug in one place can corrupt a value used somewhere else, and procedures become hard to test on their own. Passing values in as parameters and returning results keeps each procedure self-contained.

Test Your Knowledge

What is printed?

int twice ( int x )
    return 2 * x
end twice

int plusThree ( int x )
    return x + 3
end plusThree

print twice ( plusThree ( 4 ) ) + plusThree ( twice ( 4 ) )

A
B
C
D
Test Your Knowledge

What is printed?

void update ( int[ ] arr, int x )
    arr[0] ← arr[0] + 10
    x ← x + 10
end update

int[ ] nums ← {1, 2, 3}
int y ← 5
update ( nums, y )
print nums[0] + " " + y

A
B
C
D
Test Your Knowledge

What is printed?

int count ← 100

int process ( )
    int count ← 10
    for ( int k ← 1; k ≤ 3; k ← k + 1 )
        count ← count + k
    end for
    return count
end process

int result ← process ( )
print count + " " + result

A
B
C
D
Test Your Knowledge

What is printed?

int addBonus ( int score )
    score ← score + 5
    return score
end addBonus

int s ← 80
int t ← addBonus ( s )
print s + " " + t

A
B
C
D