Functions in C

Oracle Certification Program Candidate Guide

A C program consists of a set of functions with one of them called the main. Program execution begins with the main. Each function is a self contained program segment and returns a value. A function specification may contain a set of parameters. While using a function, it will be necessary to assign appropriate values to its parameters. The different type of parameter transfers between the calling and called functions will be discussed in detail later in this chapter.


6.1 FUNCTION DEFINITION AND INVOCATION

The syntax of function definition is as given below:

function-name (argument1, ..., argumentn)

;

{

) statement>

}

Here value of the expression in the return statement is returned to the calling program.

Example 6.1 :

int max (x,y) /* return maximum of two integers*/

int x,y;

{

int c;

if x > y

c=x;

else

c=y;

return (c);

}

Here the function max returns maximum of two integers. The parameters x and y are called the formal parameters of the function. When it is called, e.g., with max (a,b), the parameters a and b are called actual parameters. When the function max is evaluated, since C performs binding following call by value scheme, the value of a is assigned to x and that of b is assigned to y. Naturally a and b must be of type integers like x and y.

Example 6.2 :

long int fact (n)

int n;

{

int i;

long int prod =1

if (n > 1)

for (i = 2; i <= n; ++i)

prod * = i;

return (prod);

}

/*These functions may be called in the main as follows*/

# include

main ()

{

int a, b, c;

long int z,fact();

c = max(a,b);

z = fact (c);

:

printf(“\n Maximum = %d\n Factorial of %d is %ld\n”,c,c,z);

/*one may also use a statement such as

printf (“\n\n Maximum = %d\n”, max(a,b) ); */

}

When a function (say main) wants to utilize another function (say f), which is defined later, it should be declared in the calling function (here main). Such a declaration is called forward declaration. In its simplest form a forward declaration can be written as function-name();

In a more comprehensive system, the type of the parameters should also be specified. Such declaration is called function prototype. Although the usage of the function prototype is not mandatory, it is desirable because such prototypes help in error checking between calls to the function and corresponding function definitions.

Example 6.3 :

# include

main()

{

int a,b,c;

long int z;

int max(int,int); /*function declaration ( function prototypes) */

long int fact(int);

:

:

scanf(“%d%d”,&a,&b);

c=max(a,b);

z=fact(c);

printf(“\nMaximum : %d\n”,c);

printf(“\nThe factorial of %d is %12d\n”,c,z);

}

If the function declaration precedes function call, then it is not necessary to include a function declaration within the calling portion of the program.

Recent C compilers permit the usage of keyword void to appear as a type specifier while defining a function that does not return anything.

Example 6.4 :

void maximum (x,y)

int x,y;

{

int z;

z = (x>=y) ? x : y;

printf (“\n\n maximum value = %d”, z)

return;

}

Also one may define a function as void xyz(void) where xyz neither requires any parameter (may modify global variables) nor returns anything.

Specifying argument data type

Instead of declaring data types of the arguments after the function declaration (called forward declaration), it is possible to include such declarations within the function specification. The syntax would be

function name (type1 arg1, type2 arg2, ..., typek argk);

Here identifies the type of the value returned by the function.

Example 6.5 :

The function long int fact (int n)

can alternatively be declared as long int fact (int)

Similarly, void maximum (int a, int b)

can be declared as void maximum (int,int)

1. For each of the following functions, write the first line of the function definition and its formal parameter declaration :

(a)The function test returns an integer data.

(b) A function called quadratic_polynomial accepts four floating point arguments and returns a floating point result.

(c) A function after accepts a character and returns another character having next higher ASCII value..

(d) The function rotate accepts two integer data and a floating point data as arguments and returns a character.

(e) A function called inverse accepts a character and returns its ASCII value (an integer data).

(f) A function called compute requires two floating point data an integer data in that order, and returns a double precision value.

6.2 A SIMPLE IMPLEMENTATION SCHEME FOR SUBPROGRAM SEQUENCE CONTROL[1]

In this section, we explain how one function invokes another and the called function returns to the first. The simple subprogram call and return statement structure is common to almost all programming languages and is the subject of this section. In C, the keyword call is not explicitly used, while invoking a function (see example 6.3).

We are accustomed to view programs as hierarchies. A program is composed of a single main program, which during execution may call different subprograms (functions), which in turn may each call some other sub-subprograms, and so on, to any depth. The execution of each subprogram is expected to terminate at some point when it returns control to the program that called it. While a subprogram is being executed, execution of the calling program is temporarily suspended. When execution of the subprogram is over, the calling program resumes its execution at that point immediately following the call of the subprogram.

To implement a simple call-return control structure, it is necessary to clearly understand subprogram (function) execution. The execution of expressions and sequences of statements can readily be represented by a block of executable code at run time. The expression or statement sequence simply means execution of the code (using a hardware or software interpreter). For executing a function it is necessary to perform following additional tasks.

1. Note there is a distinction between a function definition and a function activation. The function definition is translated into a template. An activation is created each time a function is invoked, using the template created from the definition.

2. The implementation of a function activation involves two parts, a code segment containing the executable code and constants, and an activation record containing local data, parameters, and various other data items.

3. The code segments are invariant during execution. It is created by the compiler and stored as a static component in the memory. During function execution it is used but never modified no matter how many times the function is called. Every activation of the function uses the same code segment.

4. The activation record however is created anew each time the function is called, and it is destroyed when the function returns to the calling program. While the subprogram is in execution, the contents of the activation record are constantly updated as assignments are made to local variables and other data objects.

To avoid confusion by “execution of a particular statement S in the subprogram,” we should imply “execution of S during Kth activation of the subprogram.” Thus to keep track of the point at which a program is “ being executed,” we may maintain following two system-defined pointer variables- Current Instruction Pointer(CIP) and Current Environment Pointer(CEP).


I4

I5

I1

AR1:

AR1

AR2:

AR2

AR3:

AR3:

Local

variables

etc.

Local

variables

etc.

Local

variable

etc.

CIP :

CEP:

System Defined

Variables

Fig: 6.1 Activation Record and Function Call at the Beginning of Execution of your-function

Statements and expressions in a function are translated into executable instructions and stored in the code segment. Thus at any point of execution some instruction in some code segment is currently being (or just about to be) executed. This instruction is called the current instruction, and a pointer to it is maintained in the variable called the Current-Instruction Pointer, or CIP. At a particular stage of execution the machine fetches the instruction designated by the CIP, updates the CIP to point to the next instruction in sequence and then executes the instruction (which may itself change the CIP again to effect a jump to some other instruction).

Current Environment Pointer : As mentioned before, all activations of the same function use the same code segment but the activation record changes. Hence a pointer to the activation record is also needed. For example, when the instruction in the code references a variable X, that variable is maintained in the activation record. Each activation record for that subprogram would have a different data object named X. Hence, the activation record identifies the “referencing environment” of the subprogram (see fig.6 .1). A pointer to the current activation record (current referencing environment) called the Current Environment Pointer, or CEP is maintained during execution in the variable.

With the help of CEP and CIP pointers one can readily track how a program is executed. To begin with an activation record for the main program (function main) is created. The CIP is assigned a pointer to the first instruction in the code segment for the main program. The machine then begins fetching and execution of instructions as designated by the CIP.

When a function call say my-function instruction is reached, an activation record for the my-function is created and a pointer to it is assigned to the CEP. The CIP is assigned a pointer to the first instruction of the code segment of my-function, execution of instructions of my-function now commences from this point. If the my-function calls another function say your-function, new assignments are made to set the CIP and CEP for the activation of your-function.

In order to be able to return correctly from a function call, the values of the CIP and CEP must be saved somewhere by the function call instruction before the new values are assigned. When a return instruction is reached that terminates an activation of a function, the old values of the CIP and CEP that were saved when the subprogram was called must be retrieved and reinstated. This reinstatement of the old values is all that is necessary to return control to the correct activation of the calling subprogram at the correct place so that execution of the calling subprogram may continue. The associated pointer values ( CIP & CEP ) can be stored in the activation record of the subprogram being called. After the call instruction creates the activation record, it can store the old values(ip, ep) of the CIP and CEP at a particular place ( return point ) and assigns the new (ip, ep) to the CIP and CEP, thus effecting the transfer of control to the called function. Execution of the return instruction in the called function would cause fetching the old (ip, ep) from the return point and reinstating them as the values of the CIP and CEP, thus effecting the return control to the calling subprogram.

Thus the call and return instructions swap (ip, ep) values in and out of the CIP and CEP to effect transfers of control back and forth to subprograms. If execution is to be halted at some point, one can easily determine which subprogram was currently being executed (look at the CEP and CIP), and which subprogram had called it (look at the return point of the subprogram being executed), which subprogram had called that subprogram (look at its return point), and so on. A convenient data structure for implementing such a mechanism is a stack. In a stack one can access only the topmost element and the data introduced last can be accessed first . This feature clearly suits the requirement of storing activation record, instruction pointer and environment pointer values of subprograms. Note the activation record for the subprogram most recently activated will be on the top of the stack. Immediately below it will be the activation record of the subprogram which called it. Figure 6.1 shows this organization for a main program and two subprograms.

6.3 RECURSION

The recursion as the name suggests requires a function to be capable of calling itself. C supports implementation of recursive functions by allowing a function to call itself repeatedly. The following two examples illustrate the use of recursive functions for computing factorial of an integer number, and for generating numbers which are in Fibonacci sequence e.g. 1, 1, 2, 3, 5, 8, 13,........

Example 6.6 :

/*Computes factorial of n */

long int fact (int n)

{

if (n <=1)

return (1);

else

return (n * fact (n - 1));

}

Example 6.7 :

The sequence of Fibonacci numbers fn, satisfy the recurrence relation fn=fn-1 + fn-2, for n>2 and f1= f2 = 1.

#include

main()

{

int n;

printf (“Give n :”);

scanf (“%d”, &n);

printf(“\n fib(%d) = %d\n”, n,fib(n));

}

fib(m) /* computes first m numbers which are in Fibonacci sequence */

int m;

{

if (m == 0) return (1);

else if (m == 1) return(1);

else

return(fib(m-1) +fib(m-2)); /* note the recursive call ensures fib(m)=fib(m-1)+fib(m-2) */

}

The only difference between a recursive function call and an ordinary function call is that a recursive call creates a second activation of the subprogram during the lifetime of its first activation. If the second activation leads to another recursive call, then three activation’s may exist simultaneously, and so on. The only new element introduced by recursion is the multiple activations of the same function that can exist simultaneously at some point during its execution.

As mentioned before, at the time of each function call, a new activation record is created, which is subsequently destroyed upon return to the calling program. Note that, whatever we have discussed earlier in section 6.2 (see also fig.6.1) remains valid as the new activation records are created for subprograms my_function and your_function. Within my_function, we could easily have created a new activation record for my_function like the one for your_function. This implies that if instead of my_function calling your_function, my_function called itself recursively, the new activation record for my_function could just be added to the stack containing the older activation record of my_function.

Although the recursive call feature enhances the power of the language and is appealing from the programmer’s point of view, many problems, however, can be solved with the help of repetitive statements, without taking recourse to recursive function call. For example, the following code illustrates the implementation of factorial computation without recursive call.

Example 6.8 :

/* Implementation of a non recursive method to find factorial of a number */

# include

main()

{

int n,i,factorial;

printf(“Enter a number :” );

scanf(“%d”,&n);

fflush(stdin);

printf(“The number input is : %5d\n”,n);

if(n == 0)

printf(“\nThe factorial of 0 is 1\n”);

else

{

factorial=1;

for(i=1;i <= n;i++)

factorial=factorial * i;

printf(“The factorial of %5d is : %5d\n”,n,factorial);

}

}

It may be mentioned that from computational point of view it would be desirable to replace recursive function call by such repetitive statements, whenever possible. This is due to the reason that implementation of the recursive function call will lead to creation of activation of records and jumping to the function code and returning from it at the end of computation, as discussed above. Such activities will involve additional computational overhead which will not occur when the problem is solved using a repetitive block of statements. However there are many recursive functions which cannot be implemented by mere repetitive statements. To effectively substitute the recurrence relation complex data structures like a user stack are used. To illustrate this consider the following problem called “Tower of Hanoi” which requires a set of disks of increasing diameter to be moved from first peg to second with the help of third peg, so that at every stage a disk of smaller diameter can be placed over another having longer diameter. At no stage of the disk movement this ordering can be violated (Example 6.9).

Example 6.9 :

/* A program to solve the Tower of Hanoi problem */


A B C

#include

main()

{

int n;

printf(“Give n :”);

scanf(“%d”, &n);

towers (n, ‘A’, ‘B’, ‘C’);

}

towers(m, from,to,via)

int m;

char from, to,via;

{

if(m= = 1)

{ printf(“Move disk from peg %c to peg %c \n”, from, to);

return;

}

else

{

towers(m-1,from,via,to);

printf(“Move disk from peg %c to peg %c \n”, from, to);

towers (m-1, via, to, from);

return;

}

}

Remark : Is it possible to implement this problem without using recursive function call? If so, try it.

The following recursive function called Ackerman function,( for arbitrary m,n ) is difficult to implement without the help of recursive function call.

Ackerman’s function A(m,n) is defined as,

A(m,n)= n + 1 , if m=0

A(m -1,1) , if n=0

A(m-1,A(m,n-1)) , otherwise.

/* An implementation of Ackerman’s function in C */

a(m,n)

int m,n;

{

if(m == 0)

return n+1;

else

if(n == 0)

a(m-1,1);

else

a(m-1,a(m,n-1));

}2.Can you express each of the following algebraic formulae in a recursive form:

(a) y = (x1 + x2 + ..... + xn)

(b)y = 1 + 2x + 4x2 + 8x 3 + ... + 2n xn.

(c) y = ( 1 + x)n

3. Describe the output generated by each of the following programs:

a) #include

main()

{

int n =10;

int print(int n);

printf(“%d”,print(n) );

}

int print(int n)

{

if (n > 0)

return(n +print(n - 2) );

}

6.4 THE SCOPE OF A VARIABLE

The scope rules of a programming language determine the accessibility of data at different points during program execution. When an operation is to be executed it must be provided with the data on which it is to operate. The scope rules of a language determine how data may be provided to each operation, and how a result of one operation may be saved and retrieved for later use as an operand by a subsequent operation.

For example, suppose a C program contains:

A = B + 3 * C

Simple inspection indicates three operations in sequence, a multiplication, an addition, and an assignment are to be performed. One operand of the multiplication is the constant 3, but the other operands are marked only by the identifiers A,B and C. Now the variable B might correspond to a real number, an integer, a structure, or a pointer. Again perhaps the programmer has erred and B designates a string or serves as a statement label. The value of B may have been computed nearby, perhaps in the preceding statement. It is also likely that the value of B may have been computed at some point much earlier in the computation, separated by several levels of function call from the assignment where it is used. It is therefore necessary to solve the problem that would provide unique meaning to B in each execution of such an assignment statement. Because B may be a local or nonlocal variable, the solution to the problem depends on the “scope rules” for declarations. If B corresponds to a formal parameter of a function the meaning of B is related to the techniques for parameter transmission mechanisms for returning results from functions.

Each declaration or other definition of an identifier (e.g. variables or parameters) in the program text has a certain scope, called its static scope.

Here the term declaration is used to refer to a function definition, type definition, constant definition, or other means of defining a meaning for a particular identifier within a program text. A declaration creates an association in the program text between an identifier and some information about the data object or function that will be named by that identifier during program execution. The static scope of a declaration is that part of the program text where the identifier can be used to reference to a particular declaration of the identifier. A static scope rule is a rule for determining the static scope of a declaration. In C, for example, a static scope rule is used to specify that a reference to a variable X in a subprogram P refers to the declaration of X at the beginning of P, or if not declared there, then to the declaration of X at the beginning of the subprogram Q whose declaration precedes the declaration of P, and so on.

Example 6.10 : (this example illustrates the scope rules of C)

float a,b,c; /* a,b,c are external floating point variables */

main()

{

static float a; /* a is redefined and is local to main */

void dummy (void); /* b and c are external variables for main */

:

:

}

void dummy(void)

{

static int a; /* a and b are redefined and local to dummy */

int b; /* their scope is limited to the code for dummy */

:

}

Storage class

Storage class refers to the permanence of a variable and its scope within a program. C allows four different storage class specifications.

Automatic

External

Static

Register

Automatic variables

Automatic variables are always declared within a function and are local to the function in which they are declared; their scope is limited to that function. Automatic variables defined in different functions will be independent of one another, even though they may have the same name. Unless otherwise specified, a variable declared within a function is treated as an automatic variable. No keyword auto is required for such variable declarations.

Automatic variables can be initialized, assigned within a function. Such values are reassigned when the function is re-entered, These variables do not retain values once control is transferred out of its defining function.

External variables

External variables are not confined to a single function. Their scope extends from the point of declaration through the remainder of the program. Such variables may be referred to by all functions in that source file lying beyond their declaration and are therefore global to those functions. An external variable declaration must begin with keyword extern. This keyword however is not required in the definition of this variable. Storage space for external variables will not be allocated as a result of an external variable declaration and such declaration cannot include any assignment of initial values.

The declaration of an external variable is not the same as its definition.

The definition of an external variable is done in the same manner as an ordinary variable, but it must appear outside the functions that access the external variables. The definition allocates storage space for the external variables and the initial value can be assigned at the same time if required to do so.

Example 6.11 :

/* source code of program1.c */

# include

int counter=0; /* external variable definition and initialisation */

main()

{

........

}

float price_list[30]; /*another example of an external variable definition */

float calculate_price()

{

........

}

The integer variable counter and the float variable price_list are defined externally and they can be accessed in all the functions that follow from the stage of their definition. In case the same variables are to be accessed in a different source file or it is to be referred to before its definition (as in the case of price_list, if the variable is to be referred to in main) then an extern declaration becomes mandatory.

The external variable definition is done only once among all the files that make up the source program and the declaration extern of the same ensures its accessibility in the other files as well without recreating the variable once again.

Example 6.12 :

So to access price_list in main() of program1.c the program is modified as follows,

/* modified source code for program1.c*/

int counter=0;

main()

{

extern float price_list[];

:

:

}

Example 6.13 :

# define maxstudents 100 /* file 1 */

int grades [max students];

:

:

extern int grades []; /* file 2 */

float average ()

{

:

} /* end average */

Static Allocation

Sometimes it is desirable to define a variable within a function for which storage remains allocated throughout the execution of the program. For example it might be useful to maintain a local counter in a function that would indicate the number of times the function is invoked. This can be done by adding a keyword static in the variable declaration. A static internal variable is local to that function but remains in existence throughout the program’s execution. When the function is exited, a static variable retains its value. Similarly, a static external variable is also allocated storage only once, but may be referred to by any function that follows it in the source file.

Static variables can be initialized only by constants and not by expressions.

Example 6.14 :

# include

long int fibonacci (int n)

{

static long int f1=1, f2=2;

long int f;

f = (n <3)>

f1 = f2;

f2 = f;

return (f);

}

main ()

{

int m,n;

long int fibonacci (int m);.

scanf (“%d”, &n);

for (m = 1; m <=n; ++m)

printf (“\n i = %2d F = % l d”, m, fibonacci(m));

}

Register variables are defined using a keyword register in the declaration of an automatic variable or in the formal parameter of a function. There are several machine dependent restrictions on such variables. The register variables are discussed in chapter 13.

Multiple file declaration

The following example illustrates usage of external declaration when a program spans over multiple files.

Example 6.15 :

1st file

2nd file

#include

double a,b,x1,y1 const = 0.0001;

extern void reduce (void);

extern double curve(double x1);

main ()

{

double smax, ymax;

:

:

reduce ();

:

:

ymax = curve(xmax);

:

:

}.

extern double a,b,x1,y1, const;

extern double curve(double x1);

void reduce (void)

{

/* code for reduce */

return;

}

3rd file

#include

double curve (double x)

{

/* code for curve */

return (x * cos(x) );

}

4) How many times will the for-loop of the main () be executed ? Justify your answer.

int i = 5, j = 10;

void slip(void)

{

int j = 1;

static int n = - 3;

i += n++ + j++;

}

main()

{

for(; k

{

slip();

j++;

}

}

6.5 PARAMETERS AND PARAMETER TRANSMISSION

Explicitly transmitted parameters and results are the major alternative approaches for sharing data objects among functions. In the receiving functions, each data object is given a new local name through which it may be referenced. The arguments of a function may be obtained both through parameters and through nonlocal references (and less commonly, through external files). Similarly the results of a function may be returned through parameters through assignments to nonlocal variables (or files), or through explicit function values. Thus the terms argument and result apply to data sent to and returned from the functions through a variety of language constructs. In narrowing our focus to parameters and parameter transmission, the terms actual parameter and formal parameter become central.

A formal parameter is a type of local data object within a function. The function definition normally lists the names and declarations for formal parameters as part of the specification (heading). A formal parameter name is a simple identifier, and the declaration ordinarily gives the type and other attributes, as in the case of an ordinary local variable declaration. For example, the C procedure heading:

int a_fuction(int x, char y)

defines two formal parameters named x and y and declares the type of each. The declaration of a formal parameter, however, does not mean the same thing as a declaration for a variable. The formal parameter, depending upon the parameter transmission mechanism to be discussed shortly may be an alias to the actual parameter data object. It may simply contain a copy of the value of those data objects.

An actual parameter is a data object that is shared with the calling function. An actual parameter may be a local data object belonging to the calling function, e.g., fun-1. It may be a formal parameter of the calling function or it may be a nonlocal data object visible to the fun-1. It may be a result returned by a function invoked by the fun-1 and immediately transmitted to the called function. An actual parameter is represented at the point of call of the function by an expression, termed an actual parameter expression, that ordinarily has the same form as any other expression in the language.

When a function is called with an actual-parameter expression, the expression is evaluated at the time of the call, before the function is entered. The data objects that result from the evaluation of the actual-parameter expressions then become the actual-parameter transmitted to the called function.

Establishing the Correspondence

When a function is called with a list of actual parameters in C, a correspondence is established between the actual parameter and the formal parameters. The correspondence is established by pairing actual and formal parameters based on their respective positions in the actual and formal-parameter lists. The first actual and first formal parameters are paired, then the second in each list and so on. For instance the function main in example 6.3, calls the function max with actual parameter a and b. Since x and y are formal parameters of max, max (int x, int y), a is associated with x and y with b.

Methods for Transmitting Parameters

Call by name

This scheme of parameter transmission views a call as a substitution for the entire body of the subprogram. Thus each formal parameter stands for the actual evaluation of the particular actual parameter. Each reference to a formal parameter requires a revaluation of the corresponding actual parameter.

Therefore, at the point of call of the subprogram, no evaluations of the actual parameters are made until they are actually referenced in the subprogram. The parameters are transmitted unevaluated, and the called subprogram determines when, (if ever) they are to be evaluated. C does not support parameter transmission using call by name, whereas ALGOL 60 supports this scheme.

Call by reference

Call by reference is perhaps the most common parameter transmission mechanism. When a data object is transmitted as a call-by-reference parameter, a pointer to the location of the data object (i.e., its l-value) corresponding to the actual parameter is used to initialize local storage location of the associated formal parameter.

Implementation of call-by-reference parameter involves two stages:

1. In the calling subprogram, each actual-parameter expression is evaluated to give a pointer to the actual-parameter data object i.e., its l-value. A list of these pointers is stored in a common storage area that is also accessible to the function being called. Control is then transferred to the subprogram, as described in the preceding chapter; i.e., the activation record for the subprogram is created (if necessary), the return point is established, and so on.

2. In the called subprogram, the list of pointers to actual parameters is accessed in order to retrieve the appropriate r-values for the actual parameters.

During execution of the subprogram, references to formal parameter names are treated as ordinary local variable references (except that there may be a hidden pointer selection).

Although C does not explicitly support call-by- reference scheme,pointer variables in C can be used to indirectly implement call by reference. This feature will be discussed in detail when we introduce pointer data type.

Call by value

If a parameter is transmitted by value, the value (i.e., r-value) of the actual parameter is copied into the called formal parameter. The implementation mechanism is similar to the call-by-reference model except that unlike the usage of l-value in the call by reference scheme, here we rely only the r-value. C supports parameter transfer only by this scheme.

From the preceding discussion, it should be clear that with call by reference we have an alias to the actual parameter, while in call by value we have no such reference. Thus, once an actual parameter is passed by value, the formal parameter cannot change the value of the actual parameter. Any changes made in the formal parameter values during execution of the function are lost when the subprogram terminates. Therefore, when the call by value scheme is adopted the called function cannot return results to the calling function through parameters. One can only use the return statement as discussed below. A return statement in a function can return only one value. Hence when multiple data objects have to be returned as a result of function invocation, C requires pointer to such objects to be used as parameters. As discussed above, this means we transfer result data values using the call by reference scheme.

Explicit Function Values

In most languages, a single result may be returned as an explicit function value rather than as a parameter. The subprogram must be declared to be a function subprogram, and the type of the result returned must be declared as part of the subprogram specification, as in the C declaration: float fn() , which specifies fn to be a function subprogram returning a result of type float. Within a C function, the result to be returned as the function value is specified by an explicit result expression given as part of the return statement that terminates execution of the subprogram, e.g., return 2 * x indicates that the value of 2 * x is to be returned as the function value.

Parameter-Transmission Examples

The combination of parameter-transmission method with the different types of actual parameters leads to a variety of effects.

Simple variables and constants.

Example 6.16 :

/*called subprogram*/

void called_sub(int a,int *b)

{

a=a + 6;

*b=*b + 5;

printf(“%d %d”,a,*b);

return;

}

/* calling subprogram*/

void calling_sub()

{

int x,y;

x=4;

y=5;

called_sub(x,&y);

printf(“%d %d\n”,x,y);

}

The above example shows the listing of a C subprogram called_sub and calling_sub. The called_sub is invoked with two formal parameters, a, transmitted by call by value scheme, and b, transmitted by call by reference scheme. Another subprogram calling_sub calls called_sub with an integer variable, x and a pointer to an integer &y, as actual parameters. The results printed by the two printf statements at the end of execution of calling_sub are : 10 10 4 10. Let us discuss in detail about each parameter in turn.

When calling_sub invokes called_sub, the actual-parameter expressions x and &y are checked; i.e., a referencing operation is invoked to determine the current association of the variable names x and y. Each variable represents an integer data object, so the actual parameters transmitted are the r-value of x and l-value of y. Since x is being transmitted by call by value scheme, formal parameter a is treated as a local integer variable within called_sub. When function called_sub begins execution, the value of x at the time of the call is assigned as the initial value of a. Subsequently x and a have no further connection. Thus when a is assigned the new value 10, x is not changed. After the call to called_sub is complete, x still has the value 4.

Parameter y, on the other hand, is transmitted by call by reference scheme. This indicates that b is a local variable in called_sub of type pointer to integer. When called_sub begins execution, the l-value of the data object y is stored as the r-value of b. When 5 is added to the value of b, b itself does not change. Instead, each reference to b (the r-value of b, which is the 1-value of y) actually gives access to the location of the data object y. As a result the assignment to b, although it looks similar to the assignment to i, actually means something quite different. The value of actual parameter y is changed to 10. When the values of the formal parameters a and b are printed in called_sub, the results are 10 and 10. After returning to calling_sub, when the values of the corresponding actual parameters x and y are printed, only y has changed value. The value 10 assigned to a in called_sub is deleted on termination. The value of b, of course, is also lost, but this is a pointer, not the value 10.

5. Find the output generated by each of the following programs :

a. # include

main()

{

int x,ctr;

int f1(int ctr);

for(ctr = 1; ctr < = 5; ++ctr)

{

x=f1(ctr);

printf(“%d ”,x);

}

}

int f1(int a)

{

int b=0;

b += a;

return(b);

}

b. # include

main()

{

int x=0,ctr;

int f1(int ctr);

for(ctr = 1; ctr < = 10; ++ctr)

{

x =f1(ctr);

printf(“%d ”,x);

}

}

int f1(int a)

{

static int b=1;

b *= a;

return(b);

}

c. # include

main()

{

int x=0, y=1,ctr;

int f1(int x);

int f2(int y);

for(ctr=1; ctr <= 5; ++ctr)

{

y += f1(x) + f2(y);

printf(“%d”, y);

}

}

int f1(int x)

{

int y;

int f2(int z);

y=f2(x);

return(y);

}

int f2(int x)

{

static int y=1;

y * = 2;

return(y* x);

}

d. # include

int x=5;

main()

{

int ctr;

int f1(int ctr);

for(ctr=1; ctr <= 4; ++ctr)

{

x=f1(ctr);

printf(“%d”, x);

}

}

int f1(int a)

{

x += a;

return(x);

}

e. # include

int x=10, y=20;

main()

{

int a,b,ctr;

int f1(int a,int b);

for(ctr=1; ctr <= 3; ++ctr)

{

a=10 * (ctr -1);

b=2 * ctr * a;

printf(“%d %d \n”,f1(x,a), f1(y,b));

}

}

int f1(int j,int k)

{

return(j + k);

}

f. # include

int x=10, y=20;

main()

{

int ctr;

int f1(int ctr);

for(ctr=1; ctr <= 4; ++ctr)

printf(“%d \n” , f1(ctr));

}

int f1(int a)

{

int c,d;

int f2(int c);

c=f2(a);

d=(c <>

return(d);

}

int f2(int a)

{

static int mult=2;

mult *= a;

return(mult);

}

g. #include

int cnt=0;

main()

{

void f1(void);

printf(“Please enter a line of text below \n”);

f1();

printf(“ Number of characters entered %d”,cnt);

}

void f1(void)

{

char c;

if((c=getchar()) != ‘\n’)

{

++cnt;

f1();

}

return;

}


0 comments: