Saturday, July 21, 2012

CPro1 Lecture: Functions and Structured programming


5
Functions and Structured Programming
OBJECTIVE
Functions are central to C programming and to the philosophy of C program design.  You’ve already been introduced to some of C’s library functions, which are complete functions supplied as part of your compiler.  This chapter covers both user-defined functions,  which, as the name implies, are functions that you, the programmer, create and standard library function, which are supplied with the C compiler package.  You will learn
·  What a function is and what its parts are.
·  About the advantages of structured programming with functions.
·  How to create a function.
·  About the declaration of local variables in a function.
·  How to return a value from a function to the program.
·  How to pass arguments to a function.
·  Pointers as arguments to functions.
·  Type void pointers as arguments.

What Is a Function ?
A Function Defined
A function is a named, independent section of C code that performs a specific task and optionally returns a value to the calling program.  Now, look at the parts of this definition.
*   A function is named:  Each function has a unique name.  By using that name in another part of the program, you can execute the statements contained in the function.  This is known as calling the function.  A function can be called from within another function.
*   A function is independent:  A function can perform its task without interference from or interfering with other parts of the program.
*   A function performs a specific task:   A task is a discrete job that your program must perform as part of its overall operation, such as sending a line of text to a printer, sorting an array into numerical order, or calculating a cube root.
*   A function can return a value to the calling program:  When your program calls a function, the statements it contains are executed.  These statements, if desired, can pass information back to the calling program.
A Function Illustrated
Listing 5.1   A program that uses a function to calculate the cube of a number
1:   /* demonstrates a simple function */
2:   #include <stdio.h>
3:
4:   long cube(long x);
5:
6:   long input, answer;
7:
8:   main()
9:   {
10:    printf(“Enter an integer value: “);
11:    scanf(“%d”,&input);
12:    answer = cube(input);
15:    printf(“\n\nThe cube of %ld is %ld.”, input, answer);
16:  }
17:
18:  long cube(long x)
19:  {
20:    long x_cubed;
21:
22:    x_cubed = x * x * x;
23:    return x_cubed;
24:  }
Line 4 contains the function prototype
Lines 18-24 contains the function definition

How a Function Works
A C program does not execute the statements in a function until the function is called by another part of the program.  When a function is called, the program can send the function information in the form of one or more arguments.  An argument is program data needed by the function to perform its task.  The statements in the function then execute, performing whatever task it was designed to do.  When the function’s statements have finished, execution passes back to the same location in the program that called the function.  Functions can send information back to the program in the form of a return value.
Functions and Structured Programming
By using functions in your C programs, you can practice structured programming, in which individual program tasks are performed by independent sections of program code.  “Independent sections of program code” sounds just like part of the definition of functions given earlier.  Functions and structured programming are closely related.
The Advantages of Structured Programming
*       It’s easier to write a structured program because complex programming problems are broken into a number of smaller, simpler tasks.  Each task is performed by a function in which code and variables are isolated from the rest of the program.  You can make progress faster dealing one at a time with these relatively smaller tasks.
*       It’s easier to debug a structure program.  If your program has a bug (something that causes it to work improperly), a structured design makes it easy to isolate the problem to a specific section of code ( a specific function).
*       A related advantage of structured programming is the time you can save.  If you write a function to perform a certain task in one program, you quickly and easily can use it on another program that needs to execute the same task.  Even if the new program needs to accomplish a slightly different task, you often find that modifying a function you created earlier is easier than writing a new one from scratch.
Planning a Structured Program
If you’re going to write a structure program, you need to do some planning first.  This planning should take place before you write a single line of code, and usually can be done with nothing more than pencil and paper.  Your plan should be a list of the specific tasks that your program performs.  Begin with a global idea of the program’s function.  If you were planning a program to manage your name and address list, what would you want the program to do?  Here are some obvious things:
Enter new names and addresses.
Modify existing entries.
Sort entries by last name.
Print mailing labels.
With this list, you’ve divided the program into four main tasks, each of which can be assigned to a function.  Now you can go a step further, dividing these tasks into subtasks.  For example, the “Enter new names and addresses” task can be subdivided into these subtasks:
Read the existing address list from disk.
Prompt the user for one or more entries.
Add the new data to the list.
Save the updated list to disk.
Likewise, the “Modify existing entries” task can be subdivided as follows:
Read the existing address list from disk.
Modify one or more entries.
Save the updated list to disk.
Already, you should see at least one advantage of structure programming.  By carefully dividing the program into tasks, you can identify parts of the program that share common tasks.  This method of programming results in a hierarchical, or layered, program structure.  When you follow this planned approach, you quickly make a list of discrete tasks that your program needs to perform.  Then you can tackle the tasks one at a time, giving all your attention to one relatively simple task.  When that function is written and working properly, you can move on to the next task.  Before you know it, your program starts to take shape.
The Top-Down Approach
By using structured programming, C programmers take the top-down approach, where the program’s structure resembles an inverted tree.  Many times, most of the real work of the program is performed by the functions at the “tip of the branches.”  The function closer to the “trunk” primarily direct program execution among these functions.
DO / DON’T
DO       plan before starting to code.  By determining your program’s structure ahead of time, you can save time writing the code and debugging it.
DON’T  try to do everything in one function.  A single function should do a single task, such as reading information from a file.

Writing a Function
The first step in writing a function is knowing what you want the function to do.  Once you know that, the actual mechanics of writing the function are not particularly difficult.
Syntax
Function Prototype
return_type function_name (arg-type name-1,...,arg-type name-n);
Function Definition
return_type function_name (arg-type name-1,...,arg-type name-n)
{
  statements;
}
The Function Prototype
A function prototype provides the compiler with the description of a function that will be defined at a later point in the program.  The prototype includes a return type indicating the type of variable that the function will return.  It also includes the function name, which should describe what the function does.  The prototype also contains the variable types of the arguments (arg-type) that will be passed to the function.  Optionally, it can contain the names of the variables that will be passed.  A prototype always should end with a semicolon.
The Function Definition
A function definition is the actual function.  The definition contains the code that will be executed.  The function definition is composed of the function header, the function body, and optionally a return statement.
The Function Header
The first line of a function definition, called the function header, should be identical to the function prototype, with the exception of the semicolon.  A function header should not end with a semicolon.  In addition, although the argument variable names were optional in the prototype, they must be included in the function header.  The function header has three components as shown in the following diagram:
                        Function name
Function return type                  Parameter list
                              type   funcname ( parm1, . . . )
The Function Return Type
The function return type specifies the type that the function returns to the calling program.  The return type can be any of C’s data types:  char, int, long, float, or double.  You can also define a function that doesn’t return a value, a return type void. Here are some examples:
int func1(...)       /* returns a type int */
float func2(...)     /* returns a type float */
void func3(...)      /* returns nothing */
The Function Name
You can name a function anything you like, as long as you follow the rules for C variable names.  A function name must be unique (not assigned to any other function or variable).  It’s a good idea to assign a name that reflects what the function does.
The Parameter List
Many functions use arguments, which are values passed to the function when it is called.  A function needs to know what kinds of arguments to expect -- the data type of each argument.  You can pass a function any of C’s data types.  Argument type information is provided in the function header by the parameter list.
Sometimes confusion arises about the distinction between a parameter and an argument.  A parameter is an entry in a function header; it serves as a “place holder” for an argument.  A function’s parameters are fixed; they do not change during program execution.
An argument is an actual value passed to the function by the calling program.  Each time a function is called, it can be passed different arguments.  A function must be passed the same number and type of arguments each time it is called, but the argument values can be different.  In the function, the argument is accessed by using the corresponding parameter name.
DO / DON’T
DO       use a function name that describes the purpose of the function.
DON’T  pass values to a function that it doesn’t need.
DON’T  try to pass fewer (or more) arguments to a function than there are parameters!

The Function Body
The function body is enclosed in braces and follows immediately after the function header.  When a function is called, execution begins at the start of the function body and terminates (returns to the calling program) when a return statement is encountered or when execution reaches the closing brace.
Local Variables
You can declare variables within the body of a function.  Variables declared in a function are called local variables.  The term local means the variables are private to that particular function and are distinct from other variables of the same name declared elsewhere in the program.
Function Statements
There is essentially no limitation on the statements that can be included within a function.  The only thing you can’t do inside a function is define another function.  C places no length restriction on functions, but as a matter of practicality, you should keep your functions relatively short.  Rarely can you find a function longer than 25 to 30 lines of code.
Returning a Value
To return a value from a function, you use the return keyword, followed by a C expression.  When execution reaches a return statement, the expression is evaluated, and execution passes the value back to the calling program.  The return value of the function is the value of the expression.
Listing 5.2  Demonstration of using multiple return statements in a function
1:   /* demonstrates using multiple return statements in a function */
2:   #include <stdio.h>
3:   int x, y, z;
4:   int larger_of(int a, int b);
5:   main()
6:   {
7:     puts(“Enter two different integer values: “);
8:     scanf(“%d %d”,&x, &y);
9:     z = larger_of(x, y);
10:    printf(“\nThe larger value is %d.”, z);
11:  }
12:  int larger_of(int a, int b)
13:  {
14:    if (a > b)
21:      return a;
22:    else
23:      return b;
24:  }
The output for Listing 5.2 is
Enter two different integer values:
200 300
The larger value is 300.

Enter two different integer values:
400
200
The larger value is 400.
DO / DON’T
DON’T  try to return a value that has a different type than the function’s type. debugging it.
DO       use local variables whenever possible.
DON’T  let functions get too long.  If a function starts getting long, try to break it into separate, smaller tasks
DO       limit each function to a single task.
DON’T  have multiple return statements if they are not needed.  You should try to have one return when possible; however, sometimes having multiple return statements is easier and clever.
Passing Arguments to a Function
To pass arguments to a function, you list them in parentheses following the function name.  The number of arguments and the type of each argument must match the parameters in the function header and prototype.  For example, if a function is defined to take two int arguments, you must pass it exactly two int arguments -- no more, no less -- and no other type.  If you try to pass a function an incorrect number and/or type of arguments, the compiler detects it, based on the information in the function prototype.
Each argument can be any valid C expression: a constant, a variable, a mathematical or logical expression, or even another function (one with a return value).
Calling Functions
There are two ways to call a function.  Any function can be called by simply calling its name and argument list alone in a statement.  If a function has a return value, it is discarded.  The second method can be used only with functions that have a return value.  Because these functions evaluate to a value (that is, their return value), they are valid C expressions and can be used anywhere a C expression can be used.
DO / DON’T
DO       pass parameters to functions in order to make the function generic and thus reusable.
DO       take advantage of the ability to put functions into expressions.
DON’T  make an individual statement confusing by putting a bunch of functions in it.  Only put functions into your statements if they don’t make the code more confusing.
Recursion
The term recursion refers to a situation where a function calls itself either directly or indirectly.  Indirect recursion occurs when one function calls another function that then calls the first function.  C allows recursive function, and they can be useful in some situations.
DO / DON’T
DO       understand and work with recursion before you use it.
DON’T  use recursion if there will be several iterations.  (An iteration is the repetition of a program statement.)  Recursion uses many resources because the function has to remember where it is.
Where to Put Functions?
Normally, you place your function prototypes before main() and your function definitions after main().
Getting More from Functions
Passing Pointers to Functions
The default method of passing an argument to a function is by value.  Passing by value means the function is passed a copy of the argument’s value.  This method has three steps:
1.  The argument expression is evaluated.
2.  The result is copied onto the stack, a temporary storage area in memory.
3.  The function retrieves the argument’s value from the stack.
When a variable is passed to a function by value, the function has access to the variable’s  value but not to the original copy of the variable.  As a result, the code in the function cannot modify the original value.  This is the main reason by value is the default method of passing arguments.  Data outside a function is protected from inadvertent modifications.
Passing by value is possible with the basic data types (char, int, long, float, and double) and structures.  There is another way to pass an argument to a function, however, pass a pointer to the argument variable rather than the value of the variable itself.  This method of passing an argument is called passing by reference.
5.3 Passing by value and passing by reference.
1:  /* Passing arguments by value and by reference */
2:  #include <stdio.h>
3: 
4:  void by_value(int a, int b, int c);
5:  void by_ref(int *a, int *b, int *c);
6: 
7:  main()
8:  {
9:       int x = 2, y = 4, z = 6;
10:      printf(“\nBefore calling by_value(), x = %d, y = %d, z = %d.”, x, y, z);
11:      by_value(x, y, z);
12:
13:      printf(“\nAfter calling by_value(), x = %d, y = %d, z = %d.”, x, y, z);
14:
15:      by_ref (&x, &y, &z);
16:      printf(“\nAfter calling by_ref(), x = %d, y = %d, z = %d.”, x, y, z);
17:
18: }
19:
20: void by_value(int a, int b, int c)
21: {
22:      a = 0;
23:      b = 0;
24:      c = 0;
25: }
26:
27: void by_ref (int *a, int *b, int *c)
28: {
29:      *a = 0;
30:      *b = 0;
31:      *c = 0;
32: }

The output for Listing 5.3 is
Before calling by_value(), x = 2, y = 4, z = 6.
After calling by_value(), x = 2, y = 4, z = 6.
After calling by_ref(), x = 0, y = 0, z = 0.
DO / DON’T
DON’T  pass large amounts of data by value if it is not necessary.  You could run out of stack space.
DO       pass variables by value if you don't want the original value altered.
DON’T  forget that a variable passed by reference should be a pointer.  Also use the indirection operator to dereference the variable in the function.
Type void Pointers
You’ve seen the void keyword used to specify that a function either doesn’t take arguments or return a value.  The void keyword also can be used to create a “generic” pointer, a pointer that can point to any type of data object.  For example, the statement
void *x;
declares x as a generic pointer; x points to something, you just haven’t yet specified what.  The most common use for type void pointers is in declaring function parameters.  You might want to create a function that can handle different types of arguments:  you can pass it a type int one time, a type float the next time, and so on.  By declaring that the function takes a void pointer as an argument, you don’t restrict it to accepting only a single data type.  If you declare a function to take a void pointer as an argument, you can pass a function a pointer to anything. 
Although you can pass a void pointer without knowing what data type it points to, you cannot dereference the pointer.  Before the code in the function can do anything with the pointer, the data type must be known.  This is done with a type cast, which is nothing more than a way of telling the program to “treat this void pointer as a pointer to type.  If x is void pointer, you type cast it as follows:
(type*) x
where type is the appropriate data type.  To tell the program that x is a pointer to type int, write
(int *) x
To dereference the pointer --  that is, to access the int that x points to -- write
*(int *) x

Listing 5.4 Using a void pointer to pass different data types to a function.
1:  /* Using type void pointers */
2:  #include <stdio.h>
3: 
4:  void half (void *x, char type);
5: 
6:  main()
7:  {
8:     /* Initialize one variable of each type. */
9:     int i = 20;
10:    long l = 10000;
11:    float f = 12.456;
12:    double d = 123.044444;
13:
14:    /* Display their initial values */
15:    printf(“\n%d”, i);
16:    printf(“\n%ld”, l);
17:    printf(“\n%f”, f);
18:    printf(“\n%lf\n\n”, d);
20:   
21:    /* Call half for each variable */
22:    half(&i, ‘i’);
23:    half(&l, ‘l’);
24:    half(&d, ‘d’);
25:    half(&f, ‘f’);
26:   
27:    /* Display their new values */
28:    printf(“\n%d”, i);
29:    printf(“\n%ld”, l);
30:    printf(“\n%f”, f);
31:    printf(“\n%lf\n\n”, d);
30: }
31:
32: void half(void *x, char type)
33: {
34:    /*Basing on the value of type, cast the pointer x appropriately and divide by 2 */
35:    switch (type)
36:    {
37:      case ‘I’: {
38:                   *((int *)x) /= 2;
39:                   break;
40:      }
41:      case ‘l’: {
42:                   *((long *)x) /= 2;
43:                   break;
44:      }
45:      case ‘f’: {
46:                   *((float *)x) /= 2;
47:                   break;
48:      }
49:      case ‘d’: {
50:                   *((double *)x) /= 2;
51:                   break;
52:      }
53:    }
54: }   
DO / DON’T
DO       cast a void pointer when you use the value it points to.
DON’T  try to increment or decrement a void pointer.


No comments:

Post a Comment