You are on page 1of 69

CSE 121 – Advanced C Programming

Dr. Nawal El Boghdady


nawal.elboghdady@eui.edu.eg

WEEK 2: FUNCTION CALLS


Outline
𝑓 𝑥 Refresher on Functions

Function Definitions

Function Call-Stack and Frames

Random Number Generation

Storage Classes

Scoping Rules

In-class Exercises
What are Functions?
𝑓 𝑥 = 2𝑥
𝑓 𝑥, 𝑦 = 𝑥 2 + 17𝑦

They receive some input

Perform an operation on the input

Return a useful output


What do Functions in C++ look
like?
void main()

//Some stuff happens


𝑥 𝑓(𝑥) 𝑂𝑢𝑡𝑝𝑢𝑡
}
Why functions?
Functions perform a task or group of tasks and this allows me to reuse this code over and over.

It also allows me to not worry about the details of implementation of this function (if it is written by
somebody else).
In more formal terms…
A function declaration is specified this way:

<data_type_of_returned_output> <function_name> (argument1, argument2,….)

//The body of the function.

//This is where the computations performed by the function take place.

return(output_variable); //This has to be of the same data type specified in the


function heading

}
Why do we use Functions in
Programming?
We would like to take in the scores of all the students in each class and compute the following:
◦ The minimum
◦ The maximum
◦ The mean
◦ Keep taking the input until -1 was entered

What is the program code?


Why do we use Functions in
Programming?
//Ask the user to enter the quiz scores of students. If the user is done, they should enter -1

//How shall I define the array?? -> In-class exercise

printf("Please enter the scores of the students on the quiz. If you are done, enter -1.\n“);

do //we're going to use a do-while loop here. This loop exits if either the user enters -1 or the maximum limit of the array is
reached
scanf_s(“%f”, temp_score);
if (temp_score != -1)
{

score[i] = temp_score;
i++;

} while ((temp_score != -1) && (num_filled[i] < num_students)); //num_students is the variable storing the length of the array.
Why do we use Functions in
Programming?
Finding the min, and max, and computing the mean:

//Find min and max:

float min = score[0], max = score[0], sum = 0;

for (int i = 0; i < num_students; i++)


{
if (score[i] < min)
min = score[i];

if (score[i] > max[i])


max[i] = score[i];
//Compute mean:
sum += score[i]; //sum all scores for this quiz...

float mean = sum / num_students; //...and divide by the number of students who took this quiz

}
Why do we use Functions in
Programming?
So instead of all this code: I could simply replace it with:
//Find min and max:
min = compute_min(score);
float min = score[0], max = score[0], sum = 0;

for (int i = 0; i < num_students; i++) max = compute_max(score);


{

if (score[i] < min)

min = score[i]; mean = compute_mean(score);


if (score[i] > max[i])

max[i] = score[i];

//Compute mean:

sum += score[i]; //sum all scores for this quiz...

float mean = sum / num_students; //...and divide by the number of students who
took this quiz

}
In Summary, This is what we
are doing
Top-Down Design (also called stepwise refinement)
◦ Break the algorithm into subtasks
◦ Break each subtask into smaller subtasks
◦ Eventually the smaller subtasks are trivial to
implement in the programming language => we implement those in a function which can be reused
(called) many times and even shared across different programs!

A very famous example of this are ‘packages’ and ‘libraries’


◦ Statistics software (e.g. R, Python) have loads of these; people spend time coding complex functionality
into a function so that you can utilize this functionality easily in your own programs by typing only one line
of code!
Let’s take a simple example
Implement a function that outputs the minimum of three values

function header
int get_min(int val1, int val2, int val3)

int min = val1;

if(val2 < min)

min = val2;

if(val3 < min)

min = val3;

return(min);

function body
How do I use (call) this function?
void main()

int x, y, z, min;

printf(“Enter the three int numbers\n”);

scanf_s(“%d %d %d”, &x, &y, &z);

min = get_min(x,y,z);

}
Functions
In top-down design, a subtask might produce
◦ No value (just input or output for example)
◦ One value
◦ More than one value

We have seen how to implement functions that


return one value
A void-function implements a subtask that
returns no value or more than one value
Example: Converting
Temperatures
The functions just developed can be used in a program to convert Fahrenheit
temperatures to Celcius using the formula

C = (5/9) (F – 32)
◦ What is the problem with the formula above?

◦ Do you see the integer division problem?


Example: Converting
Temperatures
//Convert Fahrenheit to Celsius show_results(f_temperature, c_temperature);
#include<stdio.h>
}
float celsius(float fahrenheit); /*converts a
Fahrenheit temp float celsius(float f)
to a Celsius temp.*/ {

void show_results(float f_degrees, float c_degrees); return ((5.0 / 9.0) * (f - 32));


/*Displays output.Assumes that c_degrees Celsius is }
equivalent to
f_degrees Fahrenheit*/ void show_results(float f_degrees, float c_degrees)
{
printf("%f degrees Fahrenheit is equivalent to \n %f
int main() degrees Celsius.\n",
{ f_degrees, c_degrees);
float f_temperature, c_temperature; }

printf("Enter a temperature in Fahrenheit to convert


to Celsius: ");
scanf_s("%f", &f_temperature);

c_temperature = celsius(f_temperature);
void-Function Definition
Two main differences between void-function definitions and the definitions of functions that return one value
◦ Keyword void replaces the type of the value returned
◦ E.g. void main() {…}
◦ void means that no value is returned by the function
◦ The return statement does not include and expression
Example:
void show_results(float f_degrees, float c_degrees)

printf("%f degrees Fahrenheit is equivalent to \n %f degrees Celsius.\n",

f_degrees, c_degrees);

return;

}
Void-Function Definition
Using a void-Function

void-function calls are executable statements


◦ They do not need to be part of another statement
◦ They end with a semi-colon

Example:
show_results(32.5, 0.3);

NOT: printf(show_results(32.5, 0.3));


void-Function Calls
Mechanism is nearly the same as the function calls we have seen
◦ Argument values are substituted for the formal
parameters
◦ It is fairly common to have no parameters in void-functions
◦ In this case there will be no arguments in the function call
◦ E.g. void main() {…}
◦ Statements in function body are executed
◦ Optional return statement ends the function
◦ Return statement does not include a value to return
◦ Return statement is implicit if it is not included
void-Functions Why Use a
Return?
Is a return-statement ever needed in a void-function since no value is returned?
◦ Yes!
◦ What if a branch of an if-else statement requires that the function ends to avoid producing more output, or creating a mathematical
error?
◦ void-function in following example, avoids division by zero with a return statement
void-Functions with Example
//use return to stop program execution at this point (prevent division by 0 for
instance)

void ice_cream_division(int number, float total_weight);


//Outputs instructions for dividing total_weigth ounces of
//ice cream among number customers.
//If number is 0, nothing is done.

void ice_cream_division(int number, float total_weight)


{
double portion;

if (number == 0)
return;

portion = total_weight / number;


printf("Each one received %f ounces of ice cream.\n", portion);

}
Another example of Void-
Functions: The Main Function
The main function in a program is used like a void function…do you have to
end the program with a return-statement?
◦ Because the main function is defined to return a value of type int, the return is needed
◦ return 0 can be omitted, but many compilers still require it
Example 2
Write a program that computes the average of three floating numbers. Use modular programming
techniques (another term for functions).
Example 2
//Write a program that computes the average of three floating scanf_s("%f %f %f", &num1, &num2, &num3);
numbers. Use modular programming techniques (another term for
functions). average_value = avg(num1, num2, num3);

#include<stdio.h> printf("The average of the three numbers is: %f\n",


average_value);
//I must specify that there will be a function declaration coming
later so that my compiler knows in advance to expect it. }

//This is called the 'Function prototype'

float avg(float, float, float); //Here is where you actually define the functionality/body of the
function

float avg(float x, float y, float z)


void main()
{
{
float result = (x + y + z) / 3.0;
float num1, num2, num3, average_value;
return(result);
printf("Please enter three numbers to compute their average.\n");
}
Let’s Trace!
//Write a program that computes the average of three floating endl;
numbers. Use modular programming techniques (another term for cin >> num1 >> num2 >> num3;
functions).
average_value = avg(num1, num2, num3);

#include<iostream> cout << "The average of the three numbers is: " << average_value
<< endl;

using namespace std;


}

//I must specify that there will be a function declaration coming


later so that my compiler knows in advance to expect it. //Here is where you actually define the functionality/body of the
function
//This is called the 'Function prototype'
float avg(float x, float y, float z)
float avg(float, float, float);
{
float result = (x + y + z) / 3.0;
void main()
return(result);
{
float num1, num2, num3, average_value; }
cout << "Please enter three numbers to compute their average." <<
Built-in Functions:
#include<math.h>
Square Function
Consider a program that uses a function called square to calculate and print the squares of

the integers from 1 to 10 as follows:

1 4 9 16 25 36 49 64 81 100
Square Function
Random Function
Function that returns a (pseudo) randomly selected value.

The function rand in C is a pseudo random function, and is therefore predictable.

The values are computed based on a complex mathematical formula, but still the values *could*
be calculated so that the output is not random at all!

In some languages, you can seed the random function with the current time or some value. This
will help make the function a bit less predictable.

Very useful for sampling, randomizing the order of items, simulations, etc.
How does the randomization
process work?

0 5
1 7
𝑓(𝑥) 2 1
3 0
4 18
The rand Function
//rand function and rand seed
However, every time I
#include<stdio.h> run this, I get 41 on
#include<stdlib.h> my machine!!

int main() How is this random??


{
int v1 = rand() % 100;

printf("%d", v1);

return 0;
}
We need to seed our function with
some value that keeps changing!

0 5
1 7
𝑥 = 11
12
2
𝑓(𝑥) 2 1
3 0
4 18
Random Seed
Let’s build a number guessing game! The computer selects a random number between 1 and 10
inclusive and the player is asked to guess that number.

If the player guesses a larger number, the computer says that the secret number is smaller

If the player guesses a smaller number, the computer says that the secret number is larger

This goes on until the player guesses the correct number


Random Seed Program
/* rand example: guess the number */
#include<stdio.h> /* printf, scanf, puts, NULL */ else if (iSecret > iGuess)
#include<stdlib.h> /* srand, rand */ printf("The secret number is higher\n");
#include<time.h> /* time */
} while (iSecret != iGuess);

int main() printf("Congratulations! You have guessed the


{ secret number!\n");
int iSecret, iGuess; return 0;
}
/* initialize random seed: */
srand(time(NULL));

/* generate secret number between 1 and 10: */


iSecret = rand() % 10 + 1;

do {
//Get user's guess
printf("Guess the number (between 1 and 10
inclusive):\n");
scanf_s("%d", &iGuess);

//Check if the guessed number is greater than


the secret number:
if (iSecret < iGuess)
printf("The secret number is lower\n");
What if I want to
guess a number
between 20 to 30?
WHAT WOULD I CHANGE IN MY CODE?
How is the random number I get
actually computed?
Rand produces a random value between 0 and RAND_MAX (usually 32767)

x
0 RAND_MAX

y
r1 r2

𝑥−0 𝑦−𝑟1
𝑅𝐴𝑁𝐷𝑀𝐴𝑋 −0
= 𝑟2−𝑟1

𝑥∗ 𝑟2−𝑟1
𝑦 = 𝑅𝐴𝑁𝐷 + 𝑟1
𝑀𝐴𝑋
Changing the range of Rand to
be between 20 and 30
𝑥∗ 𝑟2−𝑟1
𝑦= + 𝑟1
𝑅𝐴𝑁𝐷𝑀𝐴𝑋

𝑥∗ 30−20
𝑦= 𝑅𝐴𝑁𝐷𝑀𝐴𝑋
+ 20
Let’s try!
/* rand example: guess the number */
#include<stdio.h> /* printf, scanf, puts, NULL */ else if (iSecret > iGuess)
#include<stdlib.h> /* srand, rand */ printf("The secret number is higher\n");
#include<time.h> /* time */
} while (iSecret != iGuess);

int main() printf("Congratulations! You have guessed the


{ secret number!\n");
int iSecret, iGuess; return 0;

/* initialize random seed: */ }


srand(time(NULL));

/* generate secret number between 20 and 30: */


iSecret = ((30 - 20) / 32767) * rand() + 20;

do {
//Get user's guess
printf("Guess the number (between 1 and 10
inclusive):\n");
scanf_s("%d", &iGuess);

//Check if the guessed number is greater than


the secret number:
if (iSecret < iGuess)
printf("The secret number is lower\n");
One More Time!
/* rand example: guess the number */ printf("The secret number is lower\n");
#include<stdio.h> /* printf, scanf, puts, NULL
*/ else if (iSecret > iGuess)
#include<stdlib.h> /* srand, rand */ printf("The secret number is higher\n");
#include<time.h> /* time */
} while (iSecret != iGuess);

int main() printf("Congratulations! You have guessed the


{ secret number!\n");
int iSecret, iGuess; return 0;

/* initialize random seed: */ }


srand(time(NULL));

/* generate secret number between 20 and 30: */


iSecret = ((30.0 – 20.0) / 32767) * rand() + 20;

do {
//Get user's guess
printf("Guess the number (between 1 and 10
inclusive):\n");
scanf_s("%d", &iGuess);

//Check if the guessed number is greater


than the secret number:
if (iSecret < iGuess)
What is the problem with the
previous output?

How likely am I to get a 20 versus a 21? Or a


22? Are they all equally likely?

Turns out, the outcomes of this function are


not equally likely!

How can we know?

We run this function 1000 of times and


compute the likelihood of each outcome.

Then we look at the *distribution* of the


outcomes
How do I do this??
Code
/* rand example: what is the //Initialize the outcomes //print the histogram
probability of outcomes from the array with zeros for (int k = lower - 1; k <
rand function? */ for (int i = lower - 1; i < upper; k++)
upper; i++) {
#include <stdio.h> /* { int counter =
printf, scanf, puts, NULL */ outcomes[i] = 0; outcomes[k];
#include <stdlib.h> /* srand, } printf("%d | ", k);
rand */
#include <time.h> /* time //generate 100 random numbers while (counter > 0)
*/ and count the occurrence of each {
outcome: printf("*");
int main() for (int j = 0; j < 100; j++) counter--;
{ {
enum { lower = 1 }; /* generate random number }
//defining constants in C between lower and upper: */
enum { upper = 10}; int r = ((upper - lower) printf("\n");
/ 32767.0) * rand() + lower; }
int outcomes[upper];
outcomes[r]++;
/* initialize random seed: */ }
srand(time(NULL));

}
Output

How can I change the numbers on the side


to reflect the actual outcomes of the rand
function?
Let’s modularize this code!
1. Function to create and initialize the array

2. Function to generate the random numbers and count their occurrence, returning the filled array.

3. Function to display the histogram.

In order to do this, we need to learn about passing by reference!

int main()

int outcomes = create_array(int n);

int hist = populate_outcomes(outcomes);

display_hist(hist);

return 0;

}
Passing by Reference
& Scoping Rules
Function Calls: Scoping Rules
When we jump into the body of a function:
◦ Variables declared inside are local to this function
◦ This means that they cannot be seen, accessed, or modified by any code outside this function.
◦ We call these variables: local variables
◦ This is the default setup, unless we explicitly state that we want a particular variable to be a global variable.

Global variable
◦ Available to more than one function as well as the main part of the program
◦ Declared outside any function body
◦ Declared outside the main function body
◦ Declared before any function that uses it

Example: const double PI = 3.14159;

double volume(double);
int main()
{…}
◦ PI is available to the main function and to function volume
Passing by Reference vs Passing
by Value
Passing by Value vs. by
Reference

Email Post

Document
Function Calls: Passing by
Value
Functions we have designed so far
◦ Create local copies of the variables being passed to them (passing by value)
◦ Can only return one and only one value.

But what if I need to return more than one value?


◦ For instance, if I want to create a function that computes the statistics of the students’ grades for each quiz?
◦ In that case, I need to return:
◦ Min value
◦ Max value
◦ Mean value
◦ Standard deviation

This requires that we use a trick: we pass the variables by reference


Function Calls: Passing by
Reference
This requires that we use a trick: we pass the variables by reference
◦ This allows us to use the arguments as both input and output
◦ Variables passed in this manner will be both visible to the calling function and the called function
◦ This means that both functions will modify the contents of those variables that are passed by reference.
◦ Passing by reference means that I pass the memory address of where my variable is stored rather than
the value stored in there.
◦ This has a special syntax of course
Passing by Reference vs Passing
by Value

Memory Address Variable Name

1000 20 x

1001

1002
Passing by Reference vs Passing
by Value

Memory Address Variable Name

1000 20 x

1001

1002
Call-by-Reference Parameters
Call-by-value is not adequate when we need a sub-task to obtain input values
◦ Call-by-value means that the formal parameters receive the values of the arguments
◦ To obtain input values, we need to change the variables that are arguments to the function
◦ Recall that we have changed the values of formal parameters in a function body, but we have not changed the arguments found in the
function call (recall the cup of coffee example)

Call-by-reference parameters allow us to change the variable used in the


function call
◦ Arguments for call-by-reference parameters must be variables, not numbers

What if we would like to return more than one value? What about an array?
Passing by Reference vs Passing
by Value
Pointers
A pointer is the memory address of a variable
Pointers are declared as:
p
float* p;
?
This identifies p as a pointer variable of type “pointer to float”.
This stores the memory address of a float variable in p.
To access the contents real memory item with address 101, we use *p.
If we only refer to p, we are accessing the address of the memory block, and not the contents
of the memory block.
Pointers Tell
Where To Find A Variable
The ? in the memory cell pointed to by p indicates that its contents are undefined after
float* p; is executed.
How do I assign a value to the memory location pointed to by p?

*p = 15.5;

p
15.5
How do we specify a Call-by-
Reference?
void plus_one(int* x); //This void plus_one(int* x)
is how I define the function
{
heading
*x = *x+1;
void main()
}
{
int n = 1;
plus_one(&n);

}
Call-By-Reference:
Example
//Program to demonstrate call-by-reference void get_numbers(int* input1, int* input2)
{
#include<stdio.h> printf("Enter two integers: ");
scanf_s("%d %d", input1, input2); //NOTE HERE: There is
void get_numbers(int *input1, int *input2); NO &!!!
//Reads two ints from the keyboard. }

void swap_values(int *v1, int *v2); void swap_values(int* v1, int* v2)
//Interchanges the values of v1 and v2 {
int temp;
void show_results(int output1, int output2);
//Shows the values of v1 and v2, in that order. temp = *v1;
*v1 = *v2;
int main() *v2 = temp;
{ }
int first_num, second_num;
void show_results(int output1, int output2)
get_numbers(&first_num, &second_num); {
swap_values(&first_num, &second_num); printf("The swapped numbers are: %d %d",
show_results(first_num, second_num); output1, output2);
}
return 0;
}
Call-By-Reference Details
Call-by-reference works almost as if the argument variable is substituted for
the formal parameter, not the argument’s value
In reality, the memory location of the argument variable is given to the formal
parameter
◦ Whatever is done to a formal parameter in the function body, is actually done to the value at the memory
location of the argument variable
Passing by Reference vs Passing
by Value

Memory Address Variable Name

1000 20 x

1001

1002
Passing by Reference vs Passing
by Value

Memory Address Variable Name

1000 20 x

1001

1002
Example: Sorting an Array
Ascendingly
We still don’t know how to pass an entire array to a function. This will be revealed when we talk
about pointers.

For now, we want to build a modular code to sort the contents of an array in an ascending order
Example: Sorting an Array
Ascendingly
//Libraries bool swapped = true; void swap(float* a, float* b)
#include <stdio.h> do {
#include<stdbool.h> { float temp = *a;
swapped = false; //assume *a = *b;
//Declare function prototype array is sorted unless I *b = temp;
void swap(float*, float*); //This is a perform a swap }
function that will be used to swap the for (int i = 0; i < size - 1;
value of two variables i++) void display_array(float a)
void display_array(float); { {
if (arr[i] > arr[i + 1]) printf("%f ", a);
void main() { }
{ swap(&arr[i], &arr[i
enum { size = 5 }; + 1]);
float arr[size]; swapped = true;
}
printf("Please enter the %d values }
to sort.\n", size); } while (swapped);

for (int i = 0; i < size; i++) for (int i = 0; i < size; i++)
{ {
scanf_s("%f", &arr[i]); display_array(arr[i]);
//Notice here I have the & }
sign again!!
} }
Example: Sorting an Array
Ascendingly Output

This is not the exact output you would get, why?


Mixed Parameter Lists
Call-by-value and call-by-reference parameters can be mixed in the same
function

Example:
void good_stuff(int* par1, int par2, double* par3);
◦ par1 and par3 are call-by-reference formal parameters
◦ Changes in par1 and par3 change the argument variable
◦ par2 is a call-by-value formal parameter
◦ Changes in par2 do not change the argument variable
Choosing Parameter Types*
How do you decide whether a call-by-reference or call-by-value formal
parameter is needed?
◦ Does the function need to change the value of the
variable used as an argument?
◦ Yes? Use a call-by-reference formal parameter
◦ No? Use a call-by-value formal parameter
Comparing
Argument-Passing
Mechanisms
//Comparing pass by reference(PBR) with pall by value (PBV): printf("n1 after function call = %d\n", n1);
printf("n2 after function call = %d\n", n2);
#include<stdio.h>
}
void do_stuff(int, int*);
//par1_value is a pass-by-value parameter
//par2_value is a pass-by-reference parameter void do_stuff(int par1_value, int* par2_ref)
{
int main() par1_value = 111;
{ printf("par1_value inside function call = %d\n",
int n1; par1_value);
int n2;
*par2_ref = 222;
n1 = 1; printf("par2_ref inside function call = %d\n", *par2_ref);
n2 = 2;
return 0;
printf("n1 before function call = %d\n", n1);
printf("n2 before function call = %d\n", n2); }

do_stuff(n1, &n2);
Output

You might also like