Functions (transfer of parameters, returned values, recursion, function pointers). A function is a named, independent section of C/C++ code that performs a specific task and optionally returns a value to the calling program. Every C/C++ program has at least one function, main(). When your program starts, main() is called automatically. main() might call other functions, some of which might call still others. A function definition consists of two aspects: prototype and proper definition. The prototype specifies how it may be used. The function prototype is a statement, which means it ends with a semicolon. It consists of the function's return-type, name, and parameter list. Function prototype syntax is: return_type function_name ( [type [parametername]]...); Entities of prototype: - function_name - this is simply a unique identifier. - parameter list - this is a set of zero or more typed identifiers used for passing values to and from the function. The parameter list is a list of all parameters and their types, separated by commas. - return_type - this specifies the type of value the function returns. A function which returns nothing should have the return type void. For example, Figure. illustrates the parts of the function prototype. This prototype declares a function named area()which returns a long and that has two parameters, both integers. The prototype long area(int, int); Figure. Function prototype. is perfectly legal, but adding parameter names makes your prototype clearer. The same function with named parameters might be long area(int length, int width); The proper definition of a function consists of the function header and its body. The header is exactly like the function prototype, except that the parameters must be named, and there is no terminating semicolon. The body of the function contains the computational steps (statements) that comprise the function. The statements are enclosed in braces. Function definition syntax is: return_type function_name ( [type parametername]...) statements; If the function returns a value, it should end with a return statement, although return
statements can legally appear anywhere in the body of the function. Every function has a return-type. If one is not explicitly designated, the return type will be int. Be sure to give every function an explicit return-type. If a function does not return a value, its type will be void. Function definition is illustrated In Figure. Figure. Function definition. Using a function involves calling it. A function call consists of the function name followed by the call operator parentheses (), inside which zero or more comma-separated arguments appear. The number of arguments should match the number of function parameters. Each argument is an expression whose type should match the type of the corresponding parameter in the function prototype. An example for the function area call is: area (, 0); When a function call is executed, the arguments are first evaluated and their resulting values are assigned to the corresponding parameters. The function body is then executed. Execution begins with the first statement after the opening brace (). Finally, the function returned value (if any) is passed to the caller. Listing. shows the definition of a simple function which calculates the area of a rectangle. Listing. A simple function. 0 long area( int length, int width); // function prototype int main(void) int len, wid; long Area; printf("input the rectangle length: "); scanf("%d", &len); printf( "Input the rectangle width: "); scanf("%d", &wid); Area = area(len, wid); printf("area = %ld", Area); return 0; // function area definition long area( int length, int width) long Area; Area = length * width; return (Area); // call of function area // header of function area
Note: Note that the syntax for parameters is similar to the syntax for defining variables: type identifier followed by the parameter name. However, it is not possible to follow a type identifier with multiple comma-separated parameters: long area (int length, width) // Wrong!. Parameters and Arguments There are three ways to pass data from one function to another: - by value - by address - by reference Passing by value is sometimes called passing by copy. When you pass an argument from one function to another, the argument's value is passed to the receiving function, but the variable itself isn't passed. The value parameter receives a copy of the value of the argument passed to it. As a result, if the function makes any changes to the parameter, this will not affect the argument. For example, inlisting the two parameters are value (copy) parameters. When the function is called and lenis passed to length, lengthis a new variable, local to the function, which copies the value of len. In the same way, width receives a copy of the value of wid. The second way of passing data to a function is passing by address. Parameters used in this case are pointers that hold addresses of arguments. When C++ passes a variable by address, in effect it passes the variable itself, which means that the receiving function can change the calling function's variable. When the calling function regains control, any variable just passed by address might be changed if the called function changed the argument. The third way to pass data to a function is passing by reference. When you pass data by reference, if the called function changes the data, C++ applies those same changes to the calling function's data. In function we use the calling function's variable under a new name (an alias).the end result of passing by reference is identical to that of passing by address. There is one exception, the syntax when you passing variables (non arrays) by reference: if you pass nonarrays by address you must precede the passed arguments with ampersands and also precede all parameters in the called function with asterisks. When passing variables by reference, you only have to precede the receiving parameters with ampersands. Here are three examples of functions using different options to pass values: - Passing by value: void func( int x ) // function passing by value printf("%d", x ); // parameter value is printed The call of function func is like: int a=; func(a); - Passing by address: // x copies the value of variable a void func( int * x ) // function passing by address printf("%d", *x ); // parameter value is obtained by // indirection The call of function func is like: int a=;
func( &a); - Passing by reference: void func( int &x ) // you pass the address of variable a // function passing by reference printf("%d", x ); // the value of parameter is obtained // directly, without indirection The call of function func is like: int a=; func( a); // you pass the variable a To observe the differences, consider Listing. It defines three functions which try to reverse values of two variables. Listing. Passing values to a function 0 0 void swap (int x, int y) // pass-by-value printf("\n == Function swap ==\n" ); printf("initial values: x=%d, y=%d \n ", x, y ); int temp = x; x = y; y = temp; printf("final values: x=%d y= %d \n ", x, y); void swap (int *x, int *y) // pass-by-address (pointers) printf("\n == Function swap ==\n" ); printf("initial values: *x=%d, *y=%d\n", *x, *y ); int temp = *x; *x = *y; *y = temp; printf("final values: *x=%d, *y=%d\n",*x, *y ); void swap (int &x, int &y) // pass-by-reference printf("\n == Function swap ==\n"); printf("initial values: x=%d, y=%d \n", x, y ); int temp = x; x = y; y = temp; printf("final values: x=%d, y=%d \n", x, y ); void main(void) int a, b; printf("a= "); scanf("%d", &a); printf("b= "); scanf(" %d ", &b); swap( a, b); printf( a= %d, b=%d\n, a, b); swap( &a, &b); printf( a= %d, b=%d, a, b ); swap( a, b); printf( a= %d, b=%d, a, b );
Remarks: swap swaps x and y, that has no effect on the arguments passed to the function, because swap receives a copy of the arguments. The change of copy does not affect the original. swap uses pointer parameters. By dereferencing the pointers, swap gets to the original values and swaps them. The call syntax of swap demands passing the addresses of variables a and b (see line ). swap uses reference parameters. The parameters become aliases of the arguments passed to the function and swap them. swap has the added advantage that its call syntax is the same as swap and involves no addressing or dereferencing.. Passing Arrays to Functions There is a significant difference between an array variable and a normal variable: an array variable is really a pointer in disguise. So when you pass an array as a parameter, you are really passing its address. This means that when you pass an array, you are effectively passing the contents of the array by address. Therefore, when you pass an array, the contents of the array can also be changed. See for example the Listing. Listing. Passing arrays to functions 0 void reading_array(int Array[]) for (int i=0; i<; i++) printf("array[ %d] =", i ); scanf("%d", &Array[i]); void printing_array(int Array[]) for (int i=0; i<; i++) printf("array[ %d] = %d\n", i, Array[i]); void main(void) int myarray[]; printf("reading values for myarray:\n"); reading_array(myarray); printf("printing myarray values:\n"); printing_array(myarray);. Symbolic Constants A function parameter may also be declared to be constant. This may be used to indicate that the function does not change the value of a parameter: int func (const int par, const int par) //...
A function may also return a constant result. For example, const char* func (void) return "const string"; The usual place for constant definition is within header files so that they can be shared by source files.. Global and Local Scope Variables have either local or global scope. Their scope determines whether or not another function can use them. Everything defined outside functions and classes is said to have a global scope. The functions swap, swap, swap(listing ) all have a global scope. Variables may also be defined at the global scope. Uninitialized global variables are automatically initialized to zero. Global entities are generally accessible everywhere in the program. Each block in a program defines a local scope. Thus the body of a function represents a local scope. The parameters of a function have the same scope as the function body. Variables defined within a local scope are visible to that scope only. A variable need only be unique within its own scope. Local scopes may be nested, in which case the inner scopes override the outer scopes. For example, in int x; // x is global void function (int x) // x is local to the body of function if (x > 0) double x; // x is local to this block //... there are three distinct scopes, each containing a distinct x. Generally, the lifetime of a variable is limited to its scope. So, for example, global variables last for the duration of program execution, while local variables are created when their scope is entered and destroyed when their scope is exited. The memory space for global variables is reserved prior to program execution commencing, whereas the memory space for local variables is allocated on the stack during program execution. In C++, global variables are legal, but they are almost never used. They are necessary when the programmer needs to make data available to many functions and he does not want to pass that data as a parameter from function to function. Note: Global variables are dangerous because they are shared data, and one function can change a global variable in a way that is invisible to another function. This can and does create bugs that are very difficult to find.. Static Variables and Functions It is often useful to confine the accessibility of a global variable or function to a single function. This is facilitated by the storage class specifier static. The static storage class specifier with a local variable preserves the last value between
successive calls to that function. A static variable acts like a local variable but has the lifetime of an external variable. Syntax of declaration is: static <data definition> ; static <function definition> ; The same argument may be applied to the global variables in this file that are for the private use of the functions in the file. For example, static int var; // static global variable A local variable in a function may also be defined as static. The variable will remain only accessible within its local scope; however, its lifetime will no longer be confined to this scope, but will instead be global. In other words, a static local variable is a global variable which is only accessible within its local scope. Like global variables, static local variables are automatically initialized to 0. Static local variables are useful when we want the value of a local variable to persist across the calls to the function in which it appears. An example of using static variable: Listing. Using static variable void func(void) static int m=; printf("initial value: m = %d\n", m); m++; printf("final value: m = %d\n", m); void main() printf( "First call of function :\n"); func(); printf("\nsecond call of function :\n" ); func(); static declaration forces the static life of variable without changing its domain. These variables are allocated in the data segment, so the allocation is throughout the execution of the program, but can be referred only in the block in which it was declared. Listing. Using static variable #include <conio.h> void func(void) int a=; static int b=; a=a+; b=b+; printf("\na=%d, b=%d", a, b); void main() func(); func(); func(); getch();
. Extern Variables and Functions Because a global variable may be defined in one file and referred to in other files, some means of telling the compiler that the variable is defined elsewhere may be needed. Otherwise, the compiler may object to the variable as undefined. This is facilitated by an extern declaration. For example, the declaration extern int size; // variable declaration informs the compiler that size is actually defined somewhere (may be later in this file or in another file). This is called a variable declaration (not definition) because it does not lead to any storage being allocated for size. It is not recommended to include an initializer for an extern variable, since this causes it to become a variable definition and have storage allocated for it: extern int size = ; // no longer a declaration! If there is another definition for size elsewhere in the program, it will eventually produce an error. Function prototypes may also be declared as extern, but this has no effect when a prototype appears at the global scope. It is more useful for declaring function prototypes inside a function. For example: double Tangent (double angle) extern double sin(double); // defined elsewhere extern double cos(double); // defined elsewhere return sin(angle) / cos(angle); Listing. Using extern variables and functions // File FILE_A.cpp #include <conio.h> extern int a; extern void func(); void main() printf("a=%d\n", a); func(); getch(); // File FILE_B.cpp int a = 0; void func() printf("\nis executed func"); printf ("\na=%d", a); Remarks: The both files, FILE_A.cpp and FILE_B.cpp, are included in the same project. Variable a is declared in file FILE_B.cpp in line ; this produce the allocation of variable on stack and its initialization. This variable can be used in FILE_A.cpp because of declaration extern from line. Both files use the same variable. Function func() is defined in FILE_B.cpp (lines -) and it can be used in FILE_A.cpp because of the declaration extern from line. The two files can be compiled separately. The links between declaration and usage of variable a and function func() are made in the linkage stage.
. Recursion A function can call itself. A function which calls itself is said to be recursive. Recursion can be direct or indirect. It is direct when a function calls itself; it is indirect recursion when a function calls another function that then calls the first function. Recursion is a general programming technique applicable to problems which can be defined in terms of them selves. Take the factorial problem which is defined as: - factorial of 0 is. - factorial of a positive number n is n times the factorial of n-. The second line clearly indicates that factorial is defined in terms of itself and hence can be expressed as a recursive function: long factorial (unsigned int n) if (n<=) return ; else return n*fact(n-); Figure. Factorial() execution trace. For n set to, Figure. provides a trace of the calls to factorial. The stack frames for these calls appear sequentially on the runtime stack, one after the other. A recursive function must have at least one termination condition which can be satisfied. Otherwise, the function will call itself indefinitely until the runtime stack overflows. The factorial function, for example, has the termination condition n<= which, when satisfied, causes the recursive calls to fold back. (Note that for a negative n this condition will never be satisfied and factorial will fail). As a general rule, all recursive functions can be rewritten using iteration. An iterative version is therefore preferred in this case: long fact (unsigned int n) long res = ; for(int i=; i<=n; i++) // or: while (n> 0) res=res * n--; res = res*i; return res; Example. General Program Structure Generally, a program consists of three parts: the preprocessing directives, the global statements, the definitions of functions. In C/C++ programming, it is considered good practice to use prototype declarations for all functions that you call. These prototypes help to ensure that the compiler can generate correct code for calling the functions, as well as allowing the compiler to catch certain mistakes you might make. Strictly speaking, however, prototypes are optional but using prototypes of functions offers the following advantages:
-it gives clear program, the programmer can easily see what names are used and how to use (how many parameters and the data types); - if function prototypes are specified, the order of defining functions is not important. Listing. I. The preprocessing directives II. Global declarations #include <conio.h> #define PI. float area(float); float length(float); int r = ; void main(void) float radius, a, l; Local declaration III. Function definitions printf("enter the radius value:"); scanf("%f", &radius); a = area(radius); l = length(radius); printf("\ncirclehas:"); printf("\nradius = %f ", radius); printf("\narea = %f ", a); printf("\nlength = %f", l); Statements printf("\ncircle has:"); printf("\nradius = %d ", r); printf("\narea = %f ", area(r)); printf("\nlength = %f", length(r)); getch(); float area(float r) float a; a = PI * r * r; return a; float length(float r) float l; l = * PI * r; return l; Remarks: The call of functions length() and area() is available in main() because of their prototypes. If prototypes are missing, functions must be defined before the main(). The header of a function must have the same signature with its prototype (the same returned type and same number and type of parameters).