return_type function_name( arg_type argument, ... ) { local_variable_type local_variables; executable statement(s); return return_value; }
int add( int a, int b ) { int sum; sum = a + b; return sum; }
Return Type
- The "return type" indicates what kind of data this function will return. In the example above,the function returns an int.
- Some languages differentiate between "subroutines", which do not return a value, and "functions", which do. In C there are no subroutines, only functions, but functions are not required to return a value. The correct way to indicate that a function does not return a value is to use the return type "void". ( This is a way of explicitly saying that the function returns nothing. )
- If no return type is given, the compiler will normally assume the function returns an int. This can cause problems if the function does not in fact return an int.
Function Name
- The function name is an identifier by which this function will be known, and obeys the same naming rules as applied to variable names ( Alphanumeric characters, beginning with alpha, maximum 31 significant characters, etc. )
Formal Parameter List
- Following the function name are a pair of parentheses containing a list of the formal parameters, ( arguments ) which receive the data passed to the function.
- The ANSI standard requires that the type of each formal parameter to be listed individually within the parentheses as shown in the above example. Even if several parameters are of the same type, each must have its type given explicitly.
- If a function takes no parameters, the parameters may be left empty. The compiler will not perform any type checking on function calls in this case. A better approach is to include the keyword "void" within the parentheses, to explicitly state that the function takes no parameters.
Function Body
- The body of the function is enclosed within curly {} braces, just as the "main" function with which we have been dealing so far, and contains the instructions that will be executed when this function is called.
- The function body starts by declaring all local variables, prior to any executable statements.
- In C++ variables can be declared any time before they are used, but in general it is still best to declare all variables that will be used at the beginning of the function, or at the beginning of the block in which they are used for very local variables. ( See scope below. )
The Return Statement
- The return statement exits the called function and returns control back to the calling function.
- Once a return statement is executed, no further instructions within the function are executed.
- A single return value ( of the appropriate type ) may be returned.
- Parentheses are allowed but not required around the return value.
- A function with a void return type will not have a return value after the return statement.
- More than one return statement may appear in a function, but only one will ever be executed by any given function call.
- ( All returns other than the last need to be controlled by logic such as "if" blocks. )
- If a function does not contain a return statement, most compilers will add one automatically at the end of the routine, and may generate a warning message. The return value, if any, is undefined in this case.
- "main( )" is technically a function, and should return 0 upon successful completion, or a non-zero value otherwise. This is typically ignored by most programmers, but some compilers will issue warning messages if main() does not contain a return statement.
int add( int a, int b ); int add( int, int );
double myfunction( int, float );
void yourfunction( void );
void func( double x, double z, double b, double c ); int main( void ) { double x( 1.0 ), y( 2.0 ), z( 3.0 ); func( x, y, z, x );
int add_two( int x ); // Adds 2 to its argument and returns the result main() { // <----------------------------------------------------------- int main( void ) int y = 5; cout << "Before calling any function, y = " << y << endl; add_two( y ); cout << "After calling the function once, y = " << y << endl; y = add_two( y ); "After calling the function twice, y = " << y << endl; return 0; } // main int add_two( int x ) { // <---------------------------------------- int add_two( int ) cout << "In function, x changed from " << x; x += 2; cout << " to " << x << endl; return x; } // add_twoOutput:
Before calling any function, y = 5 In function, x changed from 5 to 7 After calling the function once, y = 5 In function, x changed from 5 to 7 After calling the function twice, y = 7
Pass By Value
- Ordinary data types ( ints, floats, doubles, chars, etc ) are passed by value in C/C++, which means that only the numerical value is passed to the function, and used to initialize the values of the functions formal parameters.
- Under the pass-by-value mechanism, the parameter variables within a function receive a copy of the variables ( data ) passed to them.
- Any changes made to the variables within a function are local to that function only, and do not affect the variables in main ( or whatever other function called the current function. ) This is true whether the variables have the same name in both functions or whether the names are different.
Pass by Reference
- C++ introduced a new passing mechanism, pass by reference.
- Under pass by reference, the parameter variables within a function become references, or aliases, for the variables that were passed from the calling function.
- Because the local function parameters just refer to the calling function's variables, any changes made in the called function to pass-by-reference parameters WILL affect the variables in the calling function.
- The parameter in the called function is just another name for the variable in the calling function. There is only one data storage location involved, accessed through either name.
- Pass by reference is indicated by placing an ampersand ( & ) before the variable name in the function prototype and definition. Within the function the variables are treated the same as any other variables.
- Example: In the function below, A is passed by value, and any changes made to it are local to the function. B is passed by reference, and any changes made to it WILL be seen by the function that called this one.
int func1( int A, int & B ) { if( A <= 0 || B <= 0 ) return 0; A += 5; B += 10; return A / B; }
- Functions may only return a single value via the return mechanism, but can have as many pass-by-reference variables as you like, making pass-by-reference an effective method for "returning" multiple results from a single function call. ( For example, it is not uncommon for the actual "return" value to be an error code, ( zero for success or non-zero if problems occur ), and to "return" all the actual results via pass-by-reference variables. )
Passing Arrays and/or Array Elements
- When one element of an array is passed to a function, it is passed in the same manner as the type of data contained in the array. ( I.e. pass-by-value for basic types, unless the function has been written to accept its parameter by reference. )
- However when the entire array is passed, it is effectively passed by reference. ( Actually by pointer/address, not covered in this set of notes. )
- This will be covered further in the section on arrays.
( Pass by Pointer / Address )
- Pass by pointer / address is another type of data passing that you may see in C/C++ textbooks, but which is not covered in these notes. ( It is often mistakenly termed pass-by-reference in older C textbooks, because the actual pass-by-reference passing mechanism was first introduced in C++. )
- Pass by pointer / address requires use of the address operator ( & ) and the pointer dereference operator ( * ), not covered in these notes.
Ordinary Local Variables
- Most of the variables that we have used so far are ordinary local variables.
- Ordinary local variables are declared inside of a function, and outside of any braced blocks such as while or for loops.
- The scope of these variables ( the range in which they exist and are valid ) is from the point of their declaration to the end of the function.
- As a general rule, all ordinary variables are normally declared at the very beginning of a function.
- This was required in C, but is merely a programming style in C++.
- Programmers who work from printouts, and/or who have extensive programming experience in C, tend to define all their variables at the beginning of each function. This has the advantage, especially when working from printouts, of listing all variables in one place, as a sort of comprehensive inventory or checklist.
- Programmers who work mostly or exclusively on the screen, particularly those who learned to program originally in C++ and not C, may prefer to declare their variables as close to where they are first used as possible. This has the advantage of keeping variable declarations and their use on the same screen, or at least within a short distance of each other. There is also some argument for improving compiler optimization performance and for avoiding conflicts with other variables having the same names. ( See "very local variables" and "variable eclipsing" below. )
- For this class you may use either style that you wish, provided you are consistent.
- Ordinary local variables are not initialized automatically. They must be initialized by the programmer, or else they will start out with unknown random values. ( Possibly zeros in some cases, but you can't count on that. )
Function Parameters
- Function parameters have the same scope as ordinary local variables.
- The only difference is that function parameters get initialized with the values passed to them by the calling function.
Global Variables
- Global variables are declared outside of any function, ordinarily at the very beginning of the file.
- The scope of global variables is from the point of declaration down to the end of the file.
- ( Global variables can be made available to functions in other files also, but that is beyond the scope of these notes. )
- Global variables are accessible to all functions within a file ( beyond the point of declaration ), without having to be passed.
- Global variables introduce many opportunities for very hard-to-find bugs, as any function can change them and it can often be very difficult to figure out how a global variable is being changed.
- Some systems may initialize globals to zero ( see static storage below ), but you should not count on that.
- Global variables should be avoided whenever possible. Beginning programmers should not use global variables.
- Globals are most often required when using callback functions, not covered in this course. The introduction of Exceptions in C++ ( also not covered in this course ) has eliminated much of the former need for global variables.
Very Local Variables
- Variables declared within a { braced block }, such as a loop, if, or switch construct are termed very local variables.
- The scope of very local variables is from the point of declaration to the end of the block in which they are declared.
- ( They should normally be declared at the beginning of the block. )
- Programmers sometimes declare very local variables for temporary variables that are only needed within a particular block.
- This is usually done to avoid name conflict problems with other variables having the same name declared in a more general scope. ( See eclipsing below. )
- Very local variables can make code hard to read and understand, and should only be used when there is a very good reason to do so.
- One common use of very local variables is in the code fragment:
for( int i = 0; i < limit; i++ ) {
- In this case the loop counter i exists within the body of the loop, and ceases to exist when the loop exits.
- There will be no conflict between this variable i and any other declared at a more general scope. ( See below. )
- If you choose the variable declaration style of declaring all ordinary local variables at the beginning of the function, then any very local variables you declare should be declared at the beginning of the block in which they are defined.
Variable Eclipsing
- If the same variable name is used in multiple scopes ( e.g. global, local, very local ), and the scopes overlap,then in the regions of overlap the more specific variable will eclipse, or hide the more general variable(s).
- When the more specific variable goes out of scope, then the next more general variable becomes visible again, unchanged by whatever may have occured while it was eclipsed.
- ( The scope resolution operator, ::, may be used to specify a particular scope. For example, ios::fixed refers to the variable "fixed" within the scope of the ios class. )
- Example. In the code below the comments indicate which X will be printed at which locations.
void func( int ); double x( 0.0 ); // Global scope int main( void ) { cout << fixed << setprecision( 1 ) << "First x = " << x << endl; // prints the global, 0.0 int x( 42 ); // Ordinary local cout << "Second x = " << x << endl; // prints the ordinary local, 42 if( x > 40 ) { char x( 'A' ); // Very local, value = 65 cout << "Third x = " << x << endl; // Prints very local, 'A' func( x ); // Passes the very local char, converted to an int. } cout << "Fifth x = " << x << endl; // Ordinary local 42 again return 0; } void func( int x ) { // local parameter cout << "Fourth x = " << x << endl; // Local parameter, 65 return; }
Program Output:
First x = 0.0
Second x = 42
Third x = A
Fourth x = 65
Fifth x = 42
Automatic Variables
- Automatic variables, ( a.k.a. auto variables ) are stored on a data structure known as "the stack".
- The stack grows and shrinks as a program executes.
- In particular, when a new function is entered, space is allocated on the stack to store all of the local variables for that function. ( Actually space is allocated for each variable at the time when it first goes into scope, i.e. when it is declared. )
- More importantly, when the function exits, the stack space allocated to that function is freed up, and becomes available for other uses. ( The stack shrinks. )
- Local variables, function parameters, and very local variables are ordinairily auto variables stored on the stack.
- Any data stored in auto variables is lost when the variables go out of scope, i.e. when a function exits. The next time the variable comes back into scope ( i.e. when the function gets called again ), the variable is allocated new space, and re-initialized if an initialization is given.
Static Variables
- Static variables are stored in a separate storage area known as "the heap".
- Space for static variables is allocated one time only, before main( ) begins, and never expires.
- Global variables are normally static. Other variables may be declared static.
- In particular, if function variables are declared as "static", then they are only initialized once, and retain their values between function calls. ( The variables can still go out of scope, but when they come back into scope they will still retain their previous values. )
- Example: The following code will generate the output shown below the code:
void staticExampleFunction( void ); int main( void ) { for( int i = 0; i < 5; i++ ) staticExampleFunction( ); return 0; } // main void staticExampleFunction( void ) { int normalInt = 0; static int staticInt = 0; cout << "The normal int = " << ++normalInt << ". The static int = " << ++staticInt << ".\n"; return; }Output:The normal int = 1. The static int = 1. The normal int = 1. The static int = 2. The normal int = 1. The static int = 3. The normal int = 1. The static int = 4. The normal int = 1. The static int = 5.Static variables can be used to count how many times a function is called, or to perform some special behaviour the first time a function is called. ( Declare a static bool "firstTime" initialized to true. If firstTime is true, do the special code and then set firstTime to false. )
The sample function writeout.cpp asks the user for a number and where they want it written. Depending on their answer, main will pass to the writout function either cout, cerr, or an open file stream, and the function will write to the appropriate location without knowing the difference.
The following topics are not covered here, but may be found in many books on C/C++