Home | Gaming | Programming | Play Online | Contact | Keyword Query
Games++ Games & Game Programming

GAMES++
Games++ Home
Games++ Gaming
Games++ Programming
Beta Testing Games
Free Online Games
Hints & Cheats

BROWSER UTILITIES
E-mail This Page
Add to Favorites

SITE SEARCH

Web Games++

AFFILIATES
Cheat Codes
Trickster Wiki
Game Ratings
Gameboy Cheats
PlayStation Cheats
BlackBerry Games
Photoshop Tutorials
Illustrator Tutorials
ImageReady Tutorials

ADVERTISEMENT

ADVERTISEMENT

Ch 4: Functions - C++ by Metrowerks

Introduction to C++ by Metrowerks

Scope Rules

Scope rules are the rules that govern how an object may be accessed by different parts of a program. Thus far, we have touched on the three types of variables: local variables, formal parameters, and global variables. We will now focus on the scope rules that govern these three types of variables and how they relate to functions.

Local Variables

As previously discussed, local variables are variables that are declared within a function and can be accessed only from within that function. However, there is another important aspect of the local variable concept that we must consider. If a variable is declared within a block of code (a block that begins and ends with a curly brace), that variable will be local only to that block (and can only be accessed within that block). In fact, a variable local to an entire function is unusual. Remember, a variable only exists when the block containing it is active. The variable "ceases to exist" when the program exits that specific block of code. Given this, two separate functions can each use identical local variables without one affecting the other. Take a look at the following sample program:

#include 
using namespace std;
void f1();
int main()
{
    char x[]="String from main function.\n";
    cout << x;
    f1();
    cout << x;
    return 0;
}
void f1()
{
    char x[]="String from the f1 function.\n";
    cout << x;
}

The output from this program would look like this:

String from main function.
String from the f1 function.
String from main function.

The two strings, both named x, operate in different blocks, and therefore have no effect on each other.

Declaring a variable within a block of code not only restricts its scope, but also leaves your computer's random-access memory (RAM) free until the function containing the local variable is called.

A global variable, static variable, or a dynamically allocated variable are allocated memory from the heap, while a local variable is allocated from the stack. While the dynamically allocated memory can be released from the heap, other globals are permanent on the heap; they are put there when the program is launched. Various functions are moved into and out of the stack as the program flow moves to that function. Therefore they are transitory and any local allocations are made when the program flow puts something on the stack. Likewise they end as the program flow removes the function from the stack.

    Important: If a global variable and a local variable share the same name, the local variable will override the global variable within the specific block in which it is declared.

Formal Parameters

A function that uses arguments lists these in the parameter list. In a prototype it is optional to give these variables a name. However in the function declaration you must specifiy the name for the types of parameters. These variables are referred to as formal parameters. After receiving the arguments of a function, formal parameters act as local variables within that function. We'll talk specifically about arguments and their use in the next section

Formal parameters must be declared using the same variable type as the arguments they will receive. For example, in a function with the prototype:

int fun(int, float, short, double);

the parameters are listed as an int, a float, a short, and a double, but are not given names. However when used in a function definition, it looks like this:

int fun(int ter, float buoy, short stop, double fudge) { ý}

Here, ter, buoy, stop, and fudge are the named formal parameters.

Global Variables

Global variables are the antithesis of local variables. They are declared outside of all functions and can be accessed by any function in the program. These variables are helpful when several functions within a program must access the same data. However, it is better to use local variables whenever possible. There are three reasons why unnecessary global variables should be avoided:

  • A large number of global variables can cause errors within a large program. They may be accidentally modified in other parts of the program.
  • Functions should be relatively self-contained. Using unnecessary global variables makes a function less general.
  • Global variables occupy your computer's RAM the entire time the program is running, not just when a specific function is called.

Now that we've taken a closer look at the rules concerning the scope of functions, let's combine what we've just learned with functions, pointers, and arrays.

The return Statement and Function Prototypes

You have seen the return statement used in every sample program, but we have yet to discuss it in depth. The return statement performs two important tasks. First, it passes control from a function back to its calling routine. Second, it can be used to return a value from a function. We will examine both of these uses here.

Returning From a Function

As we have discussed, there are two ways a function returns to its calling routine: first, when the function reaches its closing curly brace and second, when a return statement is executed. If a function is declared with a return type other than void, the function must have a value associated with the return statement. If the function is of the return type void, the return statement may be used without a value. In void functions, the return statement is generally used as a program control structure. For example, if an argument must have a negative value, the return statement can be used:

void f1(int a, int b)
{
    if (a>0) return;
    for ( ; a; a++) b = b * a;
}

A function may require several return statements. The following function shows how multiple return statements can be used with a switch statement:

void f2()
{
    // . . . 
    switch(x){
        case 1: { cout << "Yes\n"; return; }
        case 2: { cout << "No\n"; return; }
    }
    if (z < 10) return;
    // . . .
}

Returning Values

All functions not of type void return positive or negative values. These values, however, do not necessarily have to be used by the program. If a non-void function reaches the closing curly brace before a return statement is executed, the function will return an invalid value. It is always best to have a set value returned by the function rather than allowing the function to reach the closing curly brace without resolution.

Function Prototypes

Although it may not have been obvious, we have been using function prototypes throughout this course. A function prototype simply declares a function prior to its first use. The prototype of a function can specify the function's return type, the type of its parameters, the number of its parameters, or all of these specifications. The compiler needs this information beforehand so it can generate the correct type of code when a function is called. Including parameters in the function prototype is optional. The use of parameters allows the compiler to identify parameter type mismatch errors by name, so it is always best to use them in function prototypes.

Note: A function's name and arguments make up what is known as the signature of that function. Each function in a program has a unique signature.

Arguments Passed to main()

Sometimes it is necessary to pass to the program an argument when it is started. These command line arguments are passed as arguments to main, as it is the starting point for the program flow. There are only two legal parameters to main(). They are generally named argc for argument count and argv for argument variable. The argc parameter is an integer that holds the number of arguments on the command line including the program name. The argv parameter is a pointer to an array of character pointers. argv[0] points to a program's name (remember when numbering these pointers that the first index number must be 0). The second argument is pointed to by argv[1] and so on for as many arguments as needed.

To better understand how this works, let's look at a sample program.

#include 
using namespace std;
int main(int argc, char *argv[])
{
    if(argc != 2) {
        cout << "Please try again.\n";
        return 1;
    }
    cout << "Thank you, " << argv[1] << ".\n";
    return 0;
}

The preceding program, when run from the Windows (or DOS) command line, will ask you to enter your name after the program name. For example, if the program's name is "firstname" and you were using the command prompt from drive A, you would run the program by typing "firstname " and the output would look like this:

A>firstname Joe
    Thank you, Joe.
A>

If you didn't type anything after the program name, the output would look like this:

A>firstname
    Please try again.
A>

Similarly, if you entered too many arguments, the output would also ask you to "please try again":

A>firstname Joe Smith
    Please try again.
A>

On a Macintosh with CodeWarrior you emulate command line arguments by using ccommand() as your first function, as seen below:

int main(int argc, char *argv[])
{
argc = ccommand(&argv);
}

This will call a dialog box that lets you input command line arguments as well as do redirection

Note: For now, don't worry about calling your executable program from the command line. We'll cover this in greater detail in a later lesson. All that's important at this point is that you understand the concept of argv and argc.

main() may only be entered externally. You cannot call main() within your own program and pass arguments to it that way

Headers, Recursion, and Argument Passing

Let's now take a closer look at headers. We will then study recursion. Following this, we will discuss the two approaches to argument passing.

Headers

We have discussed standard C++ headers, which contain information needed by all programs. Like any function, library functions must be prototyped before use. Headers contain the prototypes of the functions in the standard library, along with the values and definitions used by those functions. Therefore, it is necessary to include the appropriate headers in your program when using library functions. A review of your compiler's library reference manual will show you which headers must be included for each of the corresponding library functions.

Recursion

Recursion is the act of a function calling itself. When this occurs, a copy of each variable used in the function is added to the stack of operations the program will perform. A function can call itself as many times as is necessary. In most cases, an iterative version of any recursive function can be written. Recursive functions are extremely complex, and many new programmers have trouble with them at first. As part of this introductory course, your reading assignment for this lesson will include the section of your textbook about recursion.

Approaches to Argument Passing

Before moving on to some of the most important function-related topics, let's take a look at the two approaches to argument passing: call-by-value and call-by-reference. These may also be referred to as passing by value and passing by reference as well. The Call-by-Value Method of Argument Passing

In the call-by-value method, the value of an argument is copied into the formal parameter of the subroutine. With this, changes made to the parameters of the subroutine will not affect the arguments used to call it. Take a look at the following program, which cubes a number and uses the call-by-value method. Note that the argument is not affected:

#include 
using namespace std;
int cubenum(int x);
int main()
{
    int y;
    cout << "Enter an integer to be cubed: ";
    cin >> y;
cout << "The cube is " << cubenum(y);
cout << " and your original number was " << y << '.';
    return 0;
}
int cubenum(int x)
{
    x = (x)*(x)*(x);
    return x;
}

When you compile and run this program, you will see that the original value of y has not been changed. The Call-by-Reference Method of Argument Passing

The call-by reference method of passing an argument does not pass a value to the function. Instead, this method passes the address of a value to a function. Therefore, when this method of passing an argument is used, changes made to the parameters of a function will affect the argument used to call the subroutine. The way to achieve this method of argument passing is by passing a pointer to an argument, which is then passed to a function. We will now look at a sample program that cubes a number, but calls the cubenum function using a pointer for an argument and changes the original value of the variable:

#include 
using namespace std;
void cubenum(int *x);
int main()
{
    int y;
    cout << "Enter an integer to be cubed: ";
    cin >> y;
cubenum(&y); // passes the address of y to the function
cout << "The cube is " << y << '.';
return 0;
}
void cubenum(int *x)
{
    *x = (*x)*(*x)*(*x);
}

We will take a deeper look at reference parameters in a later lesson when we tackle pointers.

Other Function Considerations

We will conclude our study of functions today by looking at three important function-related features of C++: function overloading, default function arguments, and function overloading and ambiguity.

Function Overloading

As we discussed (way back in Lesson 1), one of the important attributes of any object-oriented programming language is polymorphism. One of the ways that C++ allows for polymorphism is through function overloading. Functions requiring different arguments are allowed to have the same name. Take a look at the following sample program. It shows a definition for the abs() (absolute value) function which will allow either an int or a float function to be used.

#include 
using namespace std;
int absolute(int i);
float absolute(float f);
int main()
{
    cout << absolute(-5) << '\n';
    cout << absolute(-2.3f) << '\n';
    return 0;
}
int absolute(int i)
{
    if (i<0) return -i;>
    else return i;
}
float absolute(float f)
{
    if (f<0) return -f;>
    else return f;
}

Of course, the absolute value function abs() is part of the library of functions, but this illustrates how function overloading can be used.

    Note: Every version of an overloaded function must have a unique signature.

Default Function Arguments

A function can be declared with default arguments. When this occurs, if the function is called with no arguments, a default argument is used. However, a different value may be passed to the function. The following sample program contains a function called spaces(), which inserts blank lines. The default for inserted spaces is set at five lines, but a different number can be passed to the function:

#include 
using namespace std;
void spaces(int spcnum=5);
int main()
{
    int j;
    for (j=0; j<15; j++) cout ><< j << '\n';
    spaces();
    for (j=0; j<20; j++) cout ><< j << '\n';
    spaces(10);
    return 0;
}
void spaces(int spcnum);
{
    for(; spcnum; spcnum--) cout << '\n';
}

The first call to spaces() outputs the default of five lines, while the second call to spaces() outputs a specified 10 lines.

Default arguments are common in C++ but should be used with care.

Note: You may only have the default argument in either the prototype or the function header but never in both. It is a matter of style as to where they are put, but if they are not in the prototype, clear comments should be included that explain the defaults for the programmer.

Function Overloading and Ambiguity

When overloading functions, an error specific to C++ can occur: that of ambiguity. Type conversion can occur when calling a function with an argument of a different type. For example, if a function is declared to have a float argument and a variable of type int is passed to it, type conversion will occur. Similarly, if a function is declared to have a double argument and an int is passed to it, type conversion will occur. What happens if you have an overloaded function with the possibility of two valid conversions -- one version declared to receive a float and one to receive a double? Now, assume the function is called with an int. What will happen? The compiler will not know which function to send the int to (the float or the double -- both are valid conversions) and an ambiguity error will occur. Therefore, the program will not compile.

A compiler should recognize any ambiguity between functions, but a wise programmer is careful to avoid any ambiguities.

Next Lesson

Copyright © 1998-2007, Games++ All rights reserved. | Privacy Policy