Functions
Purpose Divide the code into smaller segments that: - Perform one task, isolating the operation - Are Reusable - Internally: within one program - Externally: in other programs - Provide Abstraction: use of a complex object without knowledge of its details.
Syntax Syntax is divided into 2 (optionally 3) parts: 1. Declaration: where the function is defined 2. Invocation: where the function is “called into action” (used). 3. Prototype: where a function is “listed” in a program to allow “invoke before define” when needed (optional).
Declaration Syntax returnType name(formalArguments) { } returnType: is the data type of the value returned, or void when no value is returned. name: is an identifier chosen by the programmer.
Declaration Syntax returnType name(formalArguments) { } formalArguments: is a comma-separated list of 0 or more arguments, where each argument is: type identifier or type & identifier
Declaration Syntax Examples: void hello() { } void print(int x) { } void print(int x, int y, int z){ } float divide(int x, int y) { } string askName() { } void askV(long & a, double & b){ }
Declaration Syntax If a return type is specified, the function must return a value of the specified type. Example: int fun() { int i; ... return i; // required }
Declaration Syntax If the return type is void, the function must NOT return a value. It may use return; if needed. Example: void fun() { ... if (something) return; // optional, but allowed }
Invocation Syntax As an individual statement: name ( actualArguments ); Or, if it returns a value, as part of an expression: int x = name ( actualArguments ) * 2;
Invocation Syntax name ( actualArguments ); actualArguments: a comma-separated list of expressions. Actual Arguments must match Formal Arguments in: - Number - Type - Order
Invocation Syntax Example: void fun(int a, float b, string c){ } fun(2, 3.0, ”Hi”); //valid int a=3; fun(2*a-1, 4, ”?”); //valid fun(2, 3.0); //must have 3 acts fun(2,”Hi”,3.0); //order: int,flt,str int x=fun(2, 3.0, ”Hi”); //reType void
Invocation Syntax When the Formal Argument has a &, the corresponding actual must be a single variable of the exact same type. void fun(int &a) { } int b=2; float c=3; fun(b); //valid fun(b+2); // not just a variable fun(2); // not a variable fun(c); // must be an int variable
Formal Argument Classifications Actual Argument : arg. in the Invocation Formal Argument: arg. in the Declaration - Pass-By-Value (PBV): has no & - Pass-By-Reference (PBR): has a &
Invocation Semantics When a function is invoked: 1. For each PBV (no &) argument: a. Formals are allocated. b. Actuals are evaluated. c. Value of actuals are copied to formals. 2. For each PBR (with &) argument: Formals simply rename corresponding Actuals
Invocation Semantics When a function is invoked (continued): 3. Execution control jumps to the beginning of the function. When the function returns: 1. PBV Formals are deallocated 2. PBR Formals no longer rename Actuals 3. Execution jumps back to the invocation 4. If a value is returned, the invocation represents the value returned.
Semantics Example: execution always begins at main() void fun(int a, int & b) { a++; b--; cout << a << ” ” << b << endl; } void main() { int c=5, d=10; fun(c+2,d); cout << c << ” ” << d;
Semantics Example: vars c and d are allocated and initialized void fun(int a, int & b) { a++; b--; cout << a << ” ” << b << endl; } void main() { int c=5, d=10; fun(c+2,d); cout << c << ” ” << d;
Semantics Example: function fun() is invoked; follow the Semantics void fun(int a, int & b) { a++; b--; cout << a << ” ” << b << endl; } void main() { int c=5, d=10; fun(c+2,d); cout << c << ” ” << d;
Semantics Example: 1.a. PBV formal arguments are allocated void fun(int a, int & b) { a++; b--; cout << a << ” ” << b << endl; } void main() { int c=5, d=10; fun(c+2,d); cout << c << ” ” << d;
Semantics Example: 1.b. PBV actual arguments are evaluated: c+2 evaluates to 7 void fun(int a, int & b) { a++; b--; cout << a << ” ” << b << endl; } void main() { int c=5, d=10; fun(c+2,d); cout << c << ” ” << d;
Semantics Example: 1.c. PBV actual arg. value copied to Formal: void fun(int a, int & b) { a++; b--; cout << a << ” ” << b << endl; } void main() { int c=5, d=10; fun(c+2,d); cout << c << ” ” << d;
Semantics Example: 2. PBR Formals rename Actual: void fun(int a, int & b) { a++; b--; cout << a << ” ” << b << endl; } void main() { int c=5, d=10; fun(c+2,d); cout << c << ” ” << d;
Semantics Example: 3. Execution jumps to begin of function void fun(int a, int & b) { a++; b--; cout << a << ” ” << b << endl; } void main() { int c=5, d=10; fun(c+2,d); cout << c << ” ” << d;
Semantics Example: a is incremented void fun(int a, int & b) { a++; b--; cout << a << ” ” << b << endl; } void main() { int c=5, d=10; fun(c+2,d); cout << c << ” ” << d;
Semantics Example: b is decremented void fun(int a, int & b) { a++; b--; cout << a << ” ” << b << endl; } void main() { int c=5, d=10; fun(c+2,d); cout << c << ” ” << d;
Semantics Example: a and b are printed void fun(int a, int & b) { a++; b--; cout << a << ” ” << b << endl; } void main() { int c=5, d=10; fun(c+2,d); cout << c << ” ” << d;
Semantics } Example: exit/return: follow semantics void fun(int a, int & b) { a++; b--; cout << a << ” ” << b << endl; } void main() { int c=5, d=10; fun(c+2,d); cout << c << ” ” << d;
Semantics } Example: 1. PBV Formals are deallocated void fun(int a, int & b) { a++; b--; cout << a << ” ” << b << endl; } void main() { int c=5, d=10; fun(c+2,d); cout << c << ” ” << d;
Semantics } Example: 2. PBR Formals no longer rename void fun(int a, int & b) { a++; b--; cout << a << ” ” << b << endl; } void main() { int c=5, d=10; fun(c+2,d); cout << c << ” ” << d;
Semantics } Example: 3. Execution jumps back to Invocation void fun(int a, int & b) { a++; b--; cout << a << ” ” << b << endl; } void main() { int c=5, d=10; fun(c+2,d); cout << c << ” ” << d;
Semantics } Example: c and d are printed and program ends void fun(int a, int & b) { a++; b--; cout << a << ” ” << b << endl; } void main() { int c=5, d=10; fun(c+2,d); cout << c << ” ” << d;
Semantics } Example: IMPORTANT NOTE: - at the beginning of main: c=5, d=10 - at the end of main: c=5, d=9 - The function changed d, not c, because of & void fun(int a, int & b) { a++; b--; cout << a << ” ” << b << endl; } void main() { int c=5, d=10; fun(c+2,d); cout << c << ” ” << d;
Semantics a = a * 2; return a; } Example 2: Execution starts at main() int doubleIt(int a) { a = a * 2; return a; } void main() { int d; d = doubleIt(3) - 1; cout << d;
Semantics a = a * 2; return a; } Example 2: d is allocated (not initialized) int doubleIt(int a) { a = a * 2; return a; } void main() { int d; d = doubleIt(3) - 1; cout << d;
Semantics a = a * 2; return a; } Example 2: evaluate the Right Side of the = which includes an invocation. int doubleIt(int a) { a = a * 2; return a; } void main() { int d; d = doubleIt(3) – 1; cout << d;
Semantics a = a * 2; return a; } Example 2: 1.a. Allocate PBV Formal a int doubleIt(int a) { a = a * 2; return a; } void main() { int d; d = doubleIt(3) – 1; cout << d;
Semantics a = a * 2; return a; } Example 2: 1.b. Evaluate PBV actual (to 3) int doubleIt(int a) { a = a * 2; return a; } void main() { int d; d = doubleIt(3) – 1; cout << d;
Semantics a = a * 2; return a; } Example 2: 1.c. copy Actual to Formal (essentially: a = 3;) int doubleIt(int a) { a = a * 2; return a; } void main() { int d; d = doubleIt(3) – 1; cout << d;
Semantics a = a * 2; return a; } Example 2: 2. There are no PBR Formals int doubleIt(int a) { a = a * 2; return a; } void main() { int d; d = doubleIt(3) – 1; cout << d;
Semantics a = a * 2; return a; } Example 2: 3. Execution jumps to { int doubleIt(int a) { a = a * 2; return a; } void main() { int d; d = doubleIt(3) – 1; cout << d;
Semantics a = a * 2; return a; } Example 2: Execute the assignment int doubleIt(int a) { a = a * 2; return a; } void main() { int d; d = doubleIt(3) – 1; cout << d;
Semantics a = a * 2; return a; } Example 2: return the value of a (6) int doubleIt(int a) { a = a * 2; return a; } void main() { int d; d = doubleIt(3) – 1; cout << d;
Semantics a = a * 2; return a; } Example 2: 1. PBV Formals are deallocated int doubleIt(int a) { a = a * 2; return a; } void main() { int d; d = doubleIt(3) – 1; cout << d;
Semantics a = a * 2; return a; } Example 2: 2. There are no PBR Formals int doubleIt(int a) { a = a * 2; return a; } void main() { int d; d = doubleIt(3) – 1; cout << d;
Semantics a = a * 2; return a; } Example 2: 3. Execution returns to the invocation int doubleIt(int a) { a = a * 2; return a; } void main() { int d; d = doubleIt(3) – 1; cout << d;
Semantics a = a * 2; return a; } Example 2: 4. The Invocation now represents the value returned (6) int doubleIt(int a) { a = a * 2; return a; } void main() { int d; d = doubleIt(3) – 1; // 6 - 1 cout << d;
Semantics a = a * 2; return a; } Example 2: Finish the evaluation and assignment int doubleIt(int a) { a = a * 2; return a; } void main() { int d; d = doubleIt(3) – 1; // d=5 cout << d;
Semantics a = a * 2; return a; } Example 2: print d and end program int doubleIt(int a) { a = a * 2; return a; } void main() { int d; d = doubleIt(3) – 1; cout << d;
Semantics Example 3: multiple invocations void roja() { cout << ”red” << endl; } void azul() { cout << ”blue” << endl;} void pupura() { roja(); cout << ”Purple” << endl; azul(); } void main() { pupura();
Semantics Example 3: Execution always starts at main() void roja() { cout << ”red” << endl; } void azul() { cout << ”blue” << endl;} void pupura() { roja(); cout << ”Purple” << endl; azul(); } void main() { pupura();
Semantics Example 3: Invoke azul() void roja() { cout << ”red” << endl; } void azul() { cout << ”blue” << endl;} void pupura() { roja(); cout << ”Purple” << endl; azul(); } void main() { pupura();
Semantics Example 3: No arguments to allocate void roja() { cout << ”red” << endl; } void azul() { cout << ”blue” << endl;} void pupura() { roja(); cout << ”Purple” << endl; azul(); } void main() { pupura();
Semantics Example 3: Begin execution; print blue void roja() { cout << ”red” << endl; } void azul() { cout << ”blue” << endl;} void pupura() { roja(); cout << ”Purple” << endl; azul(); } void main() { pupura();
Semantics Example 3: } means return to invocation void roja() { cout << ”red” << endl; } void azul() { cout << ”blue” << endl;} void pupura() { roja(); cout << ”Purple” << endl; azul(); } void main() { pupura();
Semantics Example 3: No value returned, so just continue void roja() { cout << ”red” << endl; } void azul() { cout << ”blue” << endl; } void pupura() { roja(); cout << ”Purple” << endl; azul(); } void main() { pupura();
Semantics Example 3: Now invoke pupura() void roja() { cout << ”red” << endl; } void azul() { cout << ”blue” << endl; } void pupura() { roja(); cout << ”Purple” << endl; azul(); } void main() { pupura();
Semantics Example 3: No arguments to set up; just begin execution void roja() { cout << ”red” << endl; } void azul() { cout << ”blue” << endl; } void pupura() { roja(); cout << ”Purple” << endl; azul(); } void main() { pupura();
Semantics Example 3: Invoke roja() void roja() { cout << ”red” << endl; } void azul() { cout << ”blue” << endl; } void pupura() { roja(); cout << ”Purple” << endl; azul(); } void main() { pupura();
Semantics Example 3: No arguments to set up, just begin execution void roja() { cout << ”red” << endl; } void azul() { cout << ”blue” << endl; } void pupura() { roja(); cout << ”Purple” << endl; azul(); } void main() { pupura();
Semantics Example 3: Print red void roja() { cout << ”red” << endl; } void azul() { cout << ”blue” << endl; } void pupura() { roja(); cout << ”Purple” << endl; azul(); } void main() { pupura();
Semantics Example 3: End function, return to invocation void roja() { cout << ”red” << endl; } void azul() { cout << ”blue” << endl; } void pupura() { roja(); cout << ”Purple” << endl; azul(); } void main() { pupura();
Semantics Example 3: Print Purple void roja() { cout << ”red” << endl; } void azul() { cout << ”blue” << endl; } void pupura() { roja(); cout << ”Purple” << endl; azul(); } void main() { pupura();
Semantics Example 3: Invoke azul() void roja() { cout << ”red” << endl; } void azul() { cout << ”blue” << endl; } void pupura() { roja(); cout << ”Purple” << endl; azul(); } void main() { pupura();
Semantics Example 3: No arguments to set up; just begin execution void roja() { cout << ”red” << endl; } void azul() { cout << ”blue” << endl; } void pupura() { roja(); cout << ”Purple” << endl; azul(); } void main() { pupura();
Semantics Example 3: Print blue void roja() { cout << ”red” << endl; } void azul() { cout << ”blue” << endl; } void pupura() { roja(); cout << ”Purple” << endl; azul(); } void main() { pupura();
Semantics Example 3: End function; return to invocation void roja() { cout << ”red” << endl; } void azul() { cout << ”blue” << endl; } void pupura() { roja(); cout << ”Purple” << endl; azul(); } void main() { pupura();
Semantics Example 3: End function; return to invocation void roja() { cout << ”red” << endl; } void azul() { cout << ”blue” << endl; } void pupura() { roja(); cout << ”Purple” << endl; azul(); } void main() { pupura();
Semantics Example 3: End program void roja() { cout << ”red” << endl; } void azul() { cout << ”blue” << endl; } void pupura() { roja(); cout << ”Purple” << endl; azul(); } void main() { pupura();
Define Before Use! The function Declaration must come before any invocation by any other function int doubleIt(int a) { // declaration ... } void main() { int d; int d = doubleIt(3); // invocation
Define Before Use! ... Otherwise, a compiler error is detected: “Undefined identifier doubleIt” void main() { int d; int d = doubleIt(3); // invocation } int doubleIt(int a) { // declaration ...
Function Prototype Purpose: to describe a function to the compiler without declaring the function. It is a promise to the Compiler: “this function hasn’t been defined yet, but I promise it will be, by the time the Linker is finished!” The promise must include the function’s name, formal arguments, and return type.
Function Prototype Syntax: returnType name ( formalArguments ) ; It is exactly the same as the first line of the Declaration (called the Header), except: - there is a semi-colon at the end - the function’s body ( { ... }) is missing It must appear before any Invocation of the function. Semantics: None. It is simply a message to the compiler on how to translate the program.
Function Prototype Usefulness: 1. to allow functions to be declared after an invocation, as long as the prototype is before the invocation. 2. to allow a source file to invoke a function declared in a different source file (ex: in a library)
Function Prototype Example: int doubleIt(int a); // prototype void main() { int d; int d = doubleIt(3); // invocation } int doubleIt(int a) { // declaration ...
Vocabulary Term Definition Internal Reuse when a function is defined once and invoked many times within a single program External Reuse when a function is defined once and invoked by multiple programs Abstraction use of a complex object without knowledge of its details Function Declaration code that defines a function Function Invocation code that calls a function into action Function Prototype code that describes a function, allowing it to be invoked before it is defined Formal Argument argument found in a function Declaration Actual Argument argument found in a function Invocation Pass-By-Value type of Formal Argument without an ampersand Pass-By-Reference type of Formal Argument with an ampersand