One of the most important features of C++, or of almost any programming language, is your ability to define new functions. You write a large piece of software by breaking it into a collection of small functions, each with a specific purpose.
The general form of a function definition is
R name(arguments) { body }where
R is the type of result that the function returns,
name is the function's name,
arguments is a comma-separated list of the function's arguments. Each argument consists of a type followed by a name.
body is a sequence of statements that performs the actions that the function needs to do.
In the body, statement
return E;indicates that the function is finished and that its result is the value of expression E.
Here are some example functions.
// Successor(n) returns the integer that is one // larger than n. For example, successor(3) = 4. int successor(int n) { return n+1; } // Larger(x,y) returns the larger of x and y. // For example, larger(5,3) = 5. // (Larger is equivalent to the max function from the // algorithm library, except that larger requires // its arguments to be of type double.) double larger(double x, double y) { if(x > y) { return x; } else { return y; } } // Largest(x,y,z) returns the largest of // x, y and z. double largest(double x, double y, double z) { return larger(x, larger(y,z)); } // IsPrime(n) returns true of n is prime and // false if n is not prime. // // Requirement: n > 1. bool isPrime(long n) { //------------------------------------------- // We try potential factors up to the square // root of n, rounded to the nearest integer. //------------------------------------------- long s = (long)(sqrt(n) + 0.5); for(int k = 2; k <= s; k++) { if(n % k == 0) { return false; } } return true; }
As the definition of largest suggests, you should feel free to use the functions that you have defined as tools for defining other functions.
Use your defined function the same way you would a function from the library. For example,
if(isPrime(2*y+1))
{
printf("%i is prime\n", 2*y+1);
}
uses predicate
isPrime in a test.
Notice that the form of the argument 2*y+1 is not required to match the name, n, used to refer to it in the definition of isPrime. Only the types must match. For any expression E of type long, isPrime(E) yields true if the value of expression E is prime. The argument name, n, in the definition of isPrime is just a convenient way to refer to the number that is passed within the body of isPrime.
When you use a function, you are said to call that function, and expression larger(7,4) is a function call expression.
After seeing a type in front of each argument in a function definition, some beginners try to write types in function calls too. Don't do that. For example,
double z = largest(a, b, c);makes sense. But
double z = largest(double a, double b, double c);does not make sense. The compiler will not accept it. Remember that you are not defining function largest here, so get rid of the types.
Do not try to do anything special to get a function to run. A function call expression automatically runs the function. Sometimes inexperienced programmers are not sure how to get the result of a function, and write something like
isPrime(x); if(isPrime(x)) { … }believing that expression isPrime(x) in the if-statement refers to the answer produced by the statement just before it. But, since this program contains expression isPrime(x) twice, isPrime(x) is called twice. The first statement calls isPrime(x) and just throws away its result. Get rid of it and write
if(isPrime(x)) { … }
When a function is called, a new frame is created for it in the run-time stack. The frame holds all of the variables and parameters that the function uses. It is a scratch pad for the function to use. When the function returns, its frame is destroyed.
It is important to realize that a function's variables are associated with a frame, not with the function itself. When we look at recursion the distinction will become very important.
Limit each function to no more than 15 noncomment lines, excluding the heading.
Say that a noncomment line is a line that contains
at least two nonblank characters that are not
part of a comment. (Notice that a line that only
contains a brace is not a noncomment line.)
Keep your functions short. I do not want to see any function definitions with more than 15 noncomment lines, excluding the function heading. |
Don't change call-by-value parameters
A function's body should not change the value of any
of its call-by-value parameters.
If you write const in front of the type of each call-by-value parameter, then the compiler will not allow you to change it. For example, definition bool isPrime(const int n) { int m = (int)(sqrt(n) + 1); for(int i = 2; i ≤ m; i++) { if(n % m == 0) { return false; } } return true; }does not allow you to change n in the body. |
Do not write two or more loops in one function.
See the standards. |
Avoid easily avoidable duplicate calls
Consider the following.
int f(const double x) { return x*sqrt(x) + sqrt(x)/x; }Notice that it computes sqrt(x) twice for the same value x. But that is easy to avoid. int f(const double x) { double s = sqrt(x); return x*s + s/x; } |
Make a function do its whole job
A crippled function
only does part of its job. It requires its caller to do the rest
for it. Look at the following example.
// CountChars(a,f) makes a[i] = the number of times character // i occurs in file f. // // Requirements: // f must be a file that is open for reading. // a must be an array with at least 256 indices. void countChars(int a[], FILE* f) { int c = getc(f); while(c != EOF) { a[c]++; c = getc(f); } }What is wrong with that? It assumes that all variables in array a have already been set to 0. There is no mention of that in the contract! Somehow, the reader is just supposed to know that. But really, you should not add a requirement to the contract. Zeroing out array a is part of the job of getting the counts. CountChars should not require its caller to help it out. How can you make countChars zero out array a without adding a second loop to countChars? |
No extraneous parameters
Do not add parameters to a function that it does not need. If there is no sensible place for a parameter in a function's contract, that parameter might not be needed. |
Do not return a void value
If expression E has type void, do not write return E;. There is nothing to return. Just say return; or, at the end of the body, say nothing. |
Do not return a void value
If expression E has type void, do not write return E;. There is nothing to return. Just say return; or, at the end of the body, say nothing. |
Is the following function definition allowed?
int dbl(int x) return x+x;Answer
Is the following function definition allowed?
int dbl(x) { return x+x; }Answer
Rewrite the following two statements as one statement. Assume that function f has no side-effects and that variable q is not needed for anything else.
int q = f(w); int r = f(q);Answer
Suppose function g is defined as follows.
int g(int n) { if(n > 3) { return 0; } else { return 2*n+3; } }What is the value of expression g(3)? Answer
Using function g from the preceding problem, what is g(g(3))? Answer
Define a function sqr(x) that takes a real number x (type double) and returns the square of x (also of type double). For example, sqr(10.0) should return 100.0. Also write a contract for this function. Answer
Without using the abs function, define function absoluteValue(n) that takes an int n and returns its absolute value. Also write a contract for this function. Answer
A perfect number is a positive integer that is equal to the sum of its proper divisors. For example, the proper divisors of 6 are 1, 2 and 3. Since 1 + 2 + 3 = 6, 6 is a perfect number.
Define a function isPerfect(n) that takes a positive integer n and yields true of n is perfect and false if not. Also write a contract for this function.
AnswerUsing your isPerfect function from the preceding exercise, define a function nextPerfect(n) that returns the smallest perfect number that is greater than n. Assume that n is a positive integer. For example, nextPerfect(1) = 6. Answer
Suppose that function dbl is defined by
int dbl(int x) { return 2*x; }Is the following allowed in C++?
int z = 10; int y = dbl(int z);Answer
Write a C++ definition of function seconds(ms), which takes an integer value ms representing some number of milliseconds (thousandths of a second) and returns a real number that is ms in seconds. For example, seconds(2001) = 2.001. Answer