< MANIK.IN >  The Student Support Page . . .    
  Sections
Home
Activities
Downloads
Service
 

 

_ Other _
Student Support
________________
________________
________________
________________
________________
________________
 

Note: This is an exported html of the original ODT. The formatting have become mangled and the program coding has gone haywire. All the footnotes have gone to the end of the article so similar symbols clubbed together makes a mess which I won't correct because of lack of time. If possible please download the PDF version for print purposes.

Functions in C


Manik Chand Patnaik (M.Tech.)

Lecturer, (C.S.E), Roland Institute of Technology.

Friday, November 16 – 2006.


We are already well acquainted with the main() the main function of C programs. In this chapter we will build upon our experience with main() to understand the concept of functions as used in C. Though no special reference is required, 'C Programming Tutorial' (K&R version 4) by Mark Burgess a Dabs Press publication and 'C, the Complete Reference' by Herbert Schildt, a McGrawHill publication is recommended for cross-reference.


What is a function?


Function in C is a single task unit. It is a self contained block of code performing some task. A function has a given name which needs to be called to execute the task of the function. A function may take some values for its operation and it may return some value too. When a function does not take or return any value it is called a subroutine. In C however, both subroutines and functions are referred as functions. The scope of auto variables defined inside a function is limited to that function only.


Where to use a function?


Function is used to implement top-down modular programming technique§. By creation of functions we do functional partitioning of a program. A big program (monolithic) is broken down to small and manageable modules. This makes a program easy to read, test and debug.


However functions need the CPU Stack for their operation and they put an additional overhead on the CPU. The overhead is called function call overhead. (Minimal understanding required: CPU and Stack operations at a function callß).


Function categories


Four Function categories exist depending on arguments and return values.


  1. Functions with arguments and return value

      e.g. int main(int argc, char* argv[ ]) int addition(int x, int y)

  2. Functions with arguments but no return value

      e.g. void main(int argc, char* argv[ ]) void output(int x, int y)

  3. Functions with no arguments and no return values

      e.g. void main(void) can also be void main() void printText(void)

  4. Functions with no arguments but with return values

      e.g. int main(void) can also be int main() int getData(void)


Creating a Function

There are two parts in creation of a function; function declaration and function definition.


  • Function declaration:-


Generally a function is declared before it is used. The declaration is generally made at the program header. i.e. The top-most part of the program. It is generally written after the pre-processor directives. The declaration can also be written in the calling function. Generally the order in which the functions are called is used in ordering the function declaration. i.e. If function f2() is used after the use of function f1() then f2() will be declared after f1().

The function declaration is also called the function prototype.


<returnType> <functionName>(<argumentList>);


<returnType> is the datatype of the value the function returns.

<functionName> is the name of the function and it follows the Identifier naming rules.

<argumentList> contains the type and sequence of values which the function requires.

e.g. int addition( int, int);


This example function taken above takes two integers as arguments and returns one integer. Keep in mind that a function can-not return more than one value. There may be zero or more than zero arguments but the return can never be greater than a single value.

The function declaration never contains any function code. The semicolon following the function prototype is mandatory.



  • Function definition:-


It is the actual function with all the code for the tasks assigned for the functions. Functions can call other functions and how deep the function calls can go is dependent on the type of system you are working on. The function definition must match perfectly with the function declaration (a.k.a. The Function prototype).


<returnType> <functionName>(<argumentList>){

<bodyOfFunction>

}


<bodyOfFunction> It carrys the function code. Those statements which make up the body of the function. If the <returnType> is not void then a return statement is required inside the <bodyOfFunction>.


e.g. int addition(int x,int y){

return(x+y);

}


  • Arguments:-


In the above example the addition function takes two integers as parameters/arguments. The syntax can be written in two different methods.

  1. As a Function Header (This is the old method. But it is still followed)


Syntax:

<returnType> <functionName>(<datatype1>,<datatype2>, - - - -)

<datatype1><identifier1>;

<datatype2><identifier2>;

<data - - - - ><iden - - - - >;

{

<bodyOfFunction>

}

  1. Written in-line Inside the parenthesis. (The new method)


Syntax:

<returnType><functionName>(<datatype1><identifier1>,<dat- - 2><iden - - 2>, - - - )

{

<bodyOfFunction>

}

Both methods are well-supported in all major compilers.


Formal and Actual Arguments


To understand the formal and actual arguments let's take a small and simple program. Arguments are also called as parameters so they can be used interchangeably.


/*Program to calculate a plus b whole square*/


Calling Function

#include<stdio.h>

int main(){

int a,b;

printf(“Enter the two values: “);

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

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

return 0;

}






<< actual parameters (arguments)

Called Function

int compute(int x, int y){

return((x*x)+(y*y)+(2*x*y));

}


<< formal parameters (arguments)

<< the return statement


In this tiny program, the arguments (a and b) which are passed on to the function compute by the calling function are called actual arguments. Those arguments used to receive the values in the called function are called formal arguments.

Note: Even if you take a and b in the formal parameters they will be treated differently from the a and b in the calling function. This is because functional partitioning creates functional black boxes.

Note: The type, order and number of actual and formal arguments must match.


Recursion


When a function calls itself it is termed as recursion. Recursion is also called circular definition. A small snippet for getting the factorial of a number would make the concept very clear.

. . .

int main(){

int num;

printf(“Enter a number to get factorial : ”);

scanf(“%d”,&num);

printf(“Factorial of %d is %d ”,num,fact(num));

return 0;

}


int fact(int n){

if(n==1) return 1;

else return(n*fact(n-1)); /*The function is calling itself*/

}


In this example the fact function calls itself. Whenever you use recursion take good care to avoid a black hole (there should be a way to come out of the recursive call stack).

Parameter Passing (by Value and by Reference)


Generally when we pass an argument to a function it passes only the value to the function and a local copy of the variable takes the values and the function acts on those local variables only. According to the black box concept the function cannot see the actual variables of the calling function. However we can explicitly make that happen by passing a value by reference.


  • Passing by Value:-


The previous examples of a plus b whole square, the values of a and b are passed on to the function variables x and y. It is very much straight forward for primary data types like int, char and float etc. However you cannot just pass a derived or aggregate data type like this.


  • Passing by Reference:-


Passing an array:-

As we know already the array bounds are not checked by C. So we have to make sure that the array bounds are taken care of. When we pass an array as the parameter we should pass the name of the array as well as the size of the array as arguments. An example is here for illustrating the concept.


1

/* program to demonstrate pass by value of array*/


Function Prototype is here ->

2

3

4

5

#include<stdio.h>

#include<stdlib.h>

void valuePrint(int[ ],int,int);




<Notice the [ ]


6

7

8

9

10

11

12

13

int main(){

int arr1[5]={0,1,1,2,3}, num;

printf(“Enter the number of the array element to print”);

scanf(“%d”,&num);

valuePrint(arr1,5,num);

return 0;

}






<No [ ] here


14

15

16

17

void valuePrint(int ar[ ],int size,int n){

if(n<size) printf(“The element %d is: %d”,n,ar[n]);

else exit(EXIT_FAILURE);

}

<complete list


<ANSI C Code

Line no 16 may be new for you. It shows the exit function returning a failure when the boundary checking of the if statement fails. This will immediately terminate the program and clean the stack. The program flow will not go back to the calling function main().


Let's do some crazy modification to this array value printing program and see the result.


10 valuePrint(arr1,5,num);

11 printf("value of the same now:%d",arr1[num]); <Printing the same element again

12 return 0;

13 }

14

15 void valuePrint(int *ar,int size,int n){ < Look at the star here

16 if(n<size) printf("The element %d is: %d\n",n,ar[n]++); <Value is Changed here ++

17 else exit(EXIT_FAILURE);

18 }

The function here would print a value which is the original value entered while initialization. Then it would increment the value. When the control passes back to the main the value is already changed so the incremented value will be printed.

You may have a question, why the compiler not complained when the prototype and definition of the function mismatched here? Because as we have already discussed, array name is actually a pointer and by passing an array we did not create a local copy of a variable. We actually handled the same array present in the calling function.

To do the same trick with the primary data types involves the use of indirection. A short example would clarify.


1

/*program handling a pass by reference*/


2

3

4

5

6

7

8

9

10

11

12

13

#include<stdio.h>

void changeNum(int*);


int main(){

int num;

printf("Enter the number");

scanf("%d",&num);

printf("The number you entered is: %d\n",num);

changeNum(&num);

printf("value of the same now:%d",num);

return 0;

}








<Address of num is passed

14

15

16

void changeNum(int *n){

*n+=5;

}


<Address received by a pointer

<Changing the value of num

Try: Instead of doing this can we change the value of num using increment on n? Try the function snippet here:


14 void changeNum(int *n){

15 int ctr;

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

17 }


This won't change the value of num. Why? A small refresh of the pointer concept is enough. See the foot note§ if you need further clarification.

Top Down Approach:-

The Top level of the program is designed first and detailed design follows later. It is a road from simple ideas to full-fledged implementations.

§Monolithic and Modular programming:-

A large program having all sorts of tasks being done in it is called a monolithic program. Modular programming tends to divide the big program into small manageable and independent executional units. This process is called as modularization. Generally large sized programs are difficult to manage and debug. Whereas a modularized program is easier to understand, manage and debug.

The most important step of modularization involves breaking the large program into functions. Afterwards the functions can be categorized / grouped and stored in different locations. Later in this chapter we will create custom header files where this concept will take a practical perspective.

ßCPU and Stack operations at a function call:-

When a function is called the address of the next instruction is pushed to stack and the registers of cpu are set with new values specific to that function and old values are stored for later use. After the function finishes, the address stored in the stack is popped out to get to the next instruction. Again the value change in cpu occurs. This happens both for user defined and library functions. This value swapping takes much cpu resources and additionally there are stack operations.

(Minimal Understanding required: Stack).

Function Prototype is essential when the function follows the main(). Otherwise even if you omit the prototype the program would be fine. However function declaration is a good practice to follow. For big programs it enables you to keep track of the functions used in the program. You would appreciate the idea when you will encounter the lengthy programs of Graph Theory in later part of your Voyage through C.

Black box concept

It is said like that because neither the calling function nor the called function can see each other's variables and content. So it creates complete separation between different functions. This concept is analogous with a black box because any thing inside a black box won't be visible.

Indirection is referencing a variable using its address. We have two operators in C, the indirection operator denoted by * and the address operator denoted by &. Both are used in conjunction to achieve indirection. & is used to get the address of a variable and * is used to point to the address. It's a very straight forward procedure. The implementation of the array is somewhat different so the learner should keep always in mind that the “array name is actually a pointer”.

§A pointer is a variable which holds the address of other variables. So the value in a pointer happens to be an unsigned number. This unsigned number when incremented does not have to impact on any other variable. Rather than modifying the value of num, the pointer n becomes invalid by self incrementation. This is the jugglery of pointer arithmetic.

(Minimal Understanding required: pointer concepts)

 
      Site Design and Content ©2005,2006 Manik Chand Patnaik. All rights reserved. Contact webmaster for any usage related queries.