CS 126 – Principle of Programming II - Functions

C

  • Example:

#include <iostream>
using namespace std;

// Print grade for the score
void printGrade(double score){
    if (score >= 90.0)
        cout << "A" << endl;
    else if (score >= 80.0)
        cout << "B" << endl;
    else if (score >= 70.0)
        cOut << "C" << endl;
    else if (score >= 60.0)
        cout << "D" << endl;
    else
        cout << "F" << endl;
}

int main(){
    cout << "Enter a score: ";
    double score;
    cin >> score;
    cout << "The grade is ";
    printGrade(score);
    return 0;
}
  • In this example, printGrade is a void function that prints a grade based on the given score.

Value-Returning Function

  • A value-returning function returns a value of a specific type.

  • Example:

#include <iostream>
using namespace std;

// Return the grade for the score
char getGrade(double score){
    if (score >= 90.0)
        return 'A';
    else if (score >= 80.0)
        return 'B';
    else if (score >= 70.0)
        return 'C';
    else if (score >= 60.0)
        return 'D';
    else
        return 'F';
}

int main(){
    cout << "Enter a score: ";
    double score;
    cin >> score;
    cout << "The grade is ";
    cout << getGrade(score) << endl;
    return 0;
}
  • In this example, getGrade returns a character representing the grade.

Passing Arguments by Value

  • By default, arguments are passed by value to parameters when invoking a function.

  • Example:

void nPrint(char ch, int n) {
    for (int i = 0; i < n; i++)
        cout << ch;
}
  • nPrint('a', 3) prints 'a' three times.

  • The actual char parameter 'a' is passed to the parameter ch, and 3 is passed to n.

  • nPrint(3, 'a') passes 3 to ch and 'a' to n, demonstrating that the order matters.

Modularizing Code

  • Modularizing code makes it easy to maintain and debug and enables code reuse.

  • Example:

#include <iostream>
using namespace std;

// Return the gcd of two integers
int gcd(int n1, int n2) {
    int gcd = 1; // Initial gcd is 1
    int k = 2; // Possible gcd
    while (k <= n1 && k <= n2){
        if (n1 % k == 0 && n2 % k == 0)
            gcd = k; // Update gcd
        k++;
    }
    return gcd; // Return gcd
}

int main(){
    // Prompt the user to enter two integers
    cout << "Enter first integer: ";
    int n1;
    cin >> n1;
    cout << "Enter second integer: ";
    int n2;
    cin >> n2;
    cout << "The greatest common divisor for " << n1 << " and " << n2 << " is " << gcd(n1, n2) << endl;
    return 0;
}
  • This code calculates the greatest common divisor (GCD) of two integers using a separate function gcd.

Overloading Functions

  • Overloading functions enables you to define functions with the same name as long as their signatures are different (different parameter types or number of parameters).

  • Examples:

#include <iostream>
using namespace std;

// Return the max between two int values
int max(int num1, int num2){
    if (num1 > num2)
        return num1;
    else
        return num2;
}

// Find the max between two double values
double max(double num1, double num2){
    if (num1 > num2)
        return num1;
    else
        return num2;
}

// Return the max among three double values
double max(double num1, double num2, double num3){
    return max(max(num1, num2), num3);
}

int main() {
    // Invoke the max function with int parameters
    cout << "The maximum between 3 and 4 is " << max(3, 4) << endl;
    // Invoke the max function with the double parameters
    cout << "The maximum between 3.0 and 5.4 is " << max(3.0, 5.4) << endl;
    // Invoke the max function with three double parameters
    cout << "The maximum between 3.0, 5.4, and 10.14 is " << max(3.0, 5.4, 10.14) << endl;
    return 0;
}

Function Prototypes

  • A function prototype declares a function without having to implement it.

  • It specifies the function's name, return type, and parameters.

  • Example:

#include <iostream>
using namespace std;

// Function prototype
int max(int num1, int num2);
double max(double num1, double num2);
double max(double num1, double num2, double num3);

int main() {
    // Invoke the max function with int parameters
    cout << "The maximum between 3 and 4 is " << max(3, 4) << endl;
    // Invoke the max function with the double parameters
    cout << "The maximum between 3.0 and 5.4 is " << max(3.0, 5.4) << endl;
    // Invoke the max function with three double parameters
    cout << "The maximum between 3.0, 5.4, and 10.14 is " << max(3.0, 5.4, 10.14) << endl;
    return 0;
}

// Return the max between two int values
int max(int num1, int num2){
    if (num1 > num2)
        return num1;
    else
        return num2;
}

// Find the max between two double values
double max(double num1, double num2){
    if (num1 > num2)
        return num1;
    else
        return num2;
}

// Return the max among three double values
double max(double num1, double num2, double num3){
    return max(max(num1, num2), num3);
}

Default Arguments

  • You can define default values for parameters in a function.

  • If an argument is not provided when the function is called, the default value is used.

#include <iostream>
using namespace std;

// Display area of a circle
void printArea(double radius = 1) {
    double area = radius * radius * 3.14159;
    cout << "area is " << area << endl;
}

int main() {
    printArea(); // Uses default radius of 1
    printArea(4); // Uses radius of 4
    return 0;
}
  • In the above example, if printArea() is called without any arguments, radius defaults to 1.

Inline Functions

  • C++ provides inline functions for improving performance, especially for short functions.

  • The compiler may replace the function call with the actual code of the function, reducing overhead.

#include <iostream>
using namespace std;

inline void f(int month, int year) {
    cout << "month is " << month << endl;
    cout << "year is " << year << endl;
}

int main() {
    int month = 10, year = 2008;
    f(month, year); // Invoke inline function
    f(9, 2010); // Invoke inline function
    return 0;
}

Local, Global, and Static Local Variables

  • A variable can be declared as local, global, or static local.

  • Local variables: Declared inside a function or a block.

  • Global variables: Declared outside all functions.

  • Static local variables: Declared inside a function but retain their values between function calls.

The Scope of Variables in a for Loop

  • A variable declared in the initialization part of a for loop header has its scope in the entire loop.

  • A variable declared inside a for loop body has its scope limited to the loop body from its declaration to the end of the block.

  • It's best to avoid redeclaring variables in nested blocks, as it can lead to confusion.

Static Local Variables

  • After a function completes its execution, all its local variables are destroyed (also known as automatic variables).

  • Static local variables retain their values between function calls. They are permanently allocated in memory for the lifetime of the program.

#include <iostream>
using namespace std;

void t1(); // Function prototype

int main(){
    t1();
    t1();
    return 0;
}

void t1(){
    static int x = 1;
    int y = 1;
    x++;
    y++;
    cout << "x is " << x << endl;
    cout << "y is " << y << endl;
}
  • In the above example,

    • x is a static local variable, so its value is retained between calls to t1().

    • y is a regular local variable, so it is reinitialized each time t1() is called.

Passing Arguments by Reference

  • Parameters can be passed by reference, making the formal parameter an alias of the actual argument.

  • Changes made to the parameters inside the function also affect the original arguments.

Example: Passing by Value (Does Not Work for Swapping)
#include <iostream>
using namespace std;

// Attempt to swap two variables - does not work!
void swap(int n1, int n2) {
    cout << "\tInside the swap function" << endl;
    cout << "\tBefore swapping n1 is " << n1 << " n2 is " << n2 << endl;
    // Swap n1 with n2
    int temp = n1;
    n1 = n2;
    n2 = temp;
    cout << "\tAfter swapping n1 is " << n1 << " n2 is " << n2 << endl;
}

int main() {
    // Declare and initialize variables
    int num1 = 1;
    int num2 = 2;
    cout << "Before invoking the swap function, num1 is " << num1 << " and num2 is " << num2 << endl;
    // Invoke the swap function to attempt to swap two variables
    swap(num1, num2);
    cout << "After invoking the swap function, num1 is " << num1 << " and num2 is " << num2 << endl;
    return 0;
}
  • In this example, num1 and num2 are not swapped because changes to n1 and n2 do not affect num1 and num2.

Example: Passing by Reference (Works for Swapping)
#include <iostream>
using namespace std;

// Swap two variables
void swap(int& n1, int& n2) {
    cout << "\tInside the swap function" << endl;
    cout << "\tBefore swapping n1 is " << n1 << " n2 is " << n2 << endl;
    // Swap n1 with n2
    int temp = n1;
    n1 = n2;
    n2 = temp;
    cout << "\tAfter swapping n1 is " << n1 << " n2 is " << n2 << endl;
}

int main() {
    // Declare and initialize variables
    int num1 = 1;
    int num2 = 2;
    cout << "Before invoking the swap function, num1 is " << num1 << " and num2 is " << num2 << endl;
    // Invoke the swap function to attempt to swap two variables
    swap(num1, num2);
    cout << "After invoking the swap function, num1 is " << num1 << " and num2 is " << num2 << endl;
    return 0;
}
  • In this example, n1 and n2 are references to num1 and num2, so swapping n1 and n2 also swaps num1 and num2.

Constant Reference Parameters

  • You can specify a constant reference parameter to prevent its value from being changed by accident.

// Return the max between two numbers
int max(const int& num1, const int& num2) {
    int result;
    if (num1 > num2)
        result = num1;
    else
        result = num2;
    return result;
}
  • Using const int& ensures that the function cannot modify the original values of num1 and num2.