Introduction
Hello Readers, in my this article I am going to explain about the Trees in data structure. No doubt, pointers (dynamic variables) have great advantages of flexibility over the array implementation of lists. We know that lists have one limitation. They are arranged in such a way that it is necessary to move through them only one position at a time, that is there is no way to move through the list other than one node at a time. In this article we overcome this limitation by studying trees, a valuable non-linear data structure that is useful in many applications.
Definitions and Terminology
Tree is one of the most important non-linear data structures. Generally a tree structure is used to organize the data so that items of information are related by branches. One very common place where such a situation arise is ‘parent-child’ relationship. Figure-1 shows parent-child relationship among person in a family. Such a tree is known as a hypothetical family tree.
Figure 1
In figure1, each node represents a person name and each line connecting two nodes denotes a relation namely ‘parent-child’ relation between the connected nodes. Often a node is represented or identified by its label and this convention will be used in the remaining part of this book. This figure shows that the node labeled ‘Radhey’ is a parent of nodes labeled ‘Mohan’, ‘Deepak’ and ‘Sunita’. Similarly the node labeled ‘Rahul’ and ‘Kiran’ are children of ‘Simmy’.
In other words we can say that if a node ‘X’ is connected to node ‘Y’ and ‘X’ is above ‘Y’ in the tree then ‘X’ is a parent of ‘Y’ and ‘Y’ is a child of ‘X’. Similarly if a node ‘X’ is connected to node ‘Y’ and ‘Y’ is connected to ‘Z’, as shown in figure-2 and ‘X’ is above the ‘Y’ and ‘Y’ is above the ‘Z’ then ‘Y’ is a parent of ‘Z’ and ‘X’ is a grand father of ‘Z’ and ‘Z’ is a children of ‘Y’ and a grand children of ‘X’.
Also note that each tree originates from a unique node. In figure-1, the tree originates from a node ‘Radhey’. And such a node, which does not have any parent is called the ‘root’ node. In figure-1, ‘Radhey’ is the root node of the tree.
Figure 2
We can also have a tree as shown in figure- 3.
Figure 3
This tree originates from a unique node, ‘A’, which is the root node of the tree. Such types of trees are normally two-way branching. Before the detail discussion of trees, let us define a tree and some terminology that are often used during the discussion of any type of tree. Consider the figure-4.
Tree
A tree, ‘T’, may be defined as a finite set of one or more nodes such that
- There is a specially designated node, called the root, ‘R’, of the tree.
- The remaining nodes (excluding the root node) are partitioned into n ≥ 0 disjoint sets T1, T2, …., Tn and each of these sets is a tree in turn. The trees T1, T2, …., Tn are called the subtrees of the root.
Node
Each element of a tree is called a node of the tree. A node stands for the item of information plus the branches to other nodes. A tree, shown in figure-4, has 12 nodes and each item of data represent a single letter for convenience.
Degree of a node
The number of subtrees of a node is called its degree. The degree of ‘A’ is 3, of ‘C’ is 1, of ‘D’ is 1 and of ‘G’ is 2.
Leaf Nodes
Nodes that have degree zero are called leaf nodes. ‘F’, ‘I’, ‘J’, ‘K’ and ‘L’ are leaf nodes. Leaf nodes are also called as terminal nodes.
Non-terminal Nodes
Except leaf nodes, all other nodes are referred to as non-terminal nodes. ‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘G’ and ‘H’ are non-terminal nodes.
Parent/Child
The subtrees of a node ‘X’ are children of ‘X’. If ‘X’ has two children ‘Y’ and ‘Z’ then ‘X’ is said to be a parent of ‘Y’ and ‘Z’.
Sibling
Children of the same parent are said to be sibling. In figure-4, ‘B’, ‘C’ and ‘D’ are siblings. Similarly ‘J’ and ‘K’ are siblings.
Figure 4
Edge
The links between a parent and its children are also referred to as edges or branches. If ‘X’ is a father of ‘Y’ then we can define an edge as a tuple (X, Y).
Path
A collection of edges connected one node to another is often termed as a path. For example, the collection of edges {(A, C), (C, G), (G, J)} denotes a path from the node ‘A’ to ‘node ‘J’ in the tree of figure-4.
Ancestors of a node
All nodes occurring in the path from a given node ‘n’ to the root of a tree are called ancestors of ‘n’. The ancestors of a node ‘K’ are ‘A’, ‘C’ and ‘G’ and node ‘J’ is descendant of ‘C’.
Level of a node
The level of a node in a tree is defined as – the root of the tree has level 0 and level of any other node in the tree is one more than the level of its father. If a node is at level ‘i’ then its children are at level ‘i+1’. For example in the tree of figure-5, the node ‘E’ is at level 2 and ‘K’ is at level 3.
Figure 5
Depth of a Tree
The depth of a tree is the maximum level of any leaf in the tree. This equals the length of the longest path from the root to any leaf. Thus the depth of the tree shown in figure-5 is 3.
Binary Trees
In fact, binary trees are important types of tree structures that occur very often. In binary trees, any node can have at most two children (which are subtrees). Moreover, children of a node of a binary tree are called left child and right child. Figure-6 shows a typical binary tree.
In figure-6, nodes ‘A’, ‘B’, and ‘C’ have two children each. On the other hand nodes ‘D’ and ‘F’ have only one child each. Node ‘D’ has only right subtree and node ‘F’ has only left subtree. The nodes ‘E’, ‘G’, ‘H’ and ‘I’ have no subtrees and they are leaf nodes in the tree.
Figure 6
Thus a binary tree ‘T’ may also be defined as a finite set of nodes that is either empty or consists of one or more nodes such that there is a specially designated node, called the root of the tree, R, and two disjoint binary trees called the left subtree and the right subtree. A left or right subtree can be empty.
Figure-7 shows some binary trees.
(a) (b) (c) (d)
Figure 7
In figure-7(b), the root of the binary tree has an empty right subtree and in figure-7(c), the root of the binary tree has an empty left subtree. If every non-leaf node in a binary tree has non-empty left and right subtrees, the tree is termed as a strictly binary tree. Figure-7(d) shows a strictly binary tree. Similarly if a binary tree is called a completely binary tree of depth ‘d’ is strictly binary tree all of whose leaves are at level ‘d’. Figure-8 shows a complete binary tree of depth 3. This type of binary tree will be discussed later on.
Figure 8
We can also have some special kinds of binary trees, known as skewed trees. Figure-9(a) and figure-9(b) shows a skewed left binary tree and skewed right subtree respectively.
Figure 9
In a binary tree we can easily find the maximum number of nodes on any level. We know that the root node is the only node on level 1. So the maximum number of nodes on level 1 is 1. Similarly on level 2 we can have maximum two nodes and on level 3 we can have maximum four nodes. Thus on any level, say ‘i’, we can maximum 2i-1 number of nodes for i≥1.
Representation of Binary Trees in C
A binary tree can be represented in following two ways:
(i) Sequential Representation of Binary Trees
(ii) Linked List Representation of Binary Trees
Sequential Representation of Binary Trees
A binary tree can also be represented by using arrays. Consider the binary tree of Figure 10. For this we will use one array of characters ‘data’ and two array of integers ‘lchild’ and ‘rchild’. The array data[] is used to contain the data fields of the nodes of this tree, as shown in Table-1. For example, if data[i] denotes the data field of node, say ‘m’, the lchild[i] would contain the index value of the left child of node ‘m’ on array data. Similarly the rchild[i] would contain the index value of the right child of node ‘m’ on array data.
Figure 10
Table 1
For example, if data[1] is ‘B’, then lchild[1] =3 and rchild[1] = 4. This signifies that the 1st node contains ‘B’ as its data; its left child is the node indexed by ‘3’, that is ‘D’, and right child is the node indexed by ‘4’, that is ‘E’. Assume that by convention, the root node is always obtained at an index 0 in this array.
We can also represent a binary tree using a single array. In this we use the numbering scheme where the nodes are numbered sequentially level by level. Nodes in the same level are numbered from left to right. Figure 11 shows the level-wise numbering of the nodes of the tree of figure10. The numbers are enclosed in circular figure.
Figure 11
While numbering nodes one should also number the empty nodes. This figure shows that for i=0, the left child is at 1 and the right child is at 2. Similarly for i=1, the left child is at 3 and the right child is at 4. Thus we can say that if the number appearing against one node ‘n’ is ‘i’ then the number appearing against the left and right children of ‘n’ are ‘2i+1’ and ‘2i+2’ respectively. Now if we store these nodes in an array tree[] then the number appearing against the nodes may act as indices of the nodes in this array. Table-2 shows the array representation for tree shown in figure-11.10.
Table 2
In table-2, unutilized space is marked by dash entries. For complete binary trees, the array representation is ideal, as no space is wasted. However if the tree is not complete binary tree then there will be a lot of unutilized space. Therefore array representation will not always be efficient in terms of space. Another limitation of array representation of binary tree is that insertion and deletion of nodes from the middle of a tree require a lot of movement of many nodes to reflect the change in level of these nodes. These problems are overcome by linked representation of binary trees.
Linked List Representation of Binary Trees
We can also represent a binary tree using a linked list. In a linked list representation of binary trees, each node has three fields – lchild, data and rchild, as shown in figure-12. The ‘lchild’ pointer points to the left subtree and the ‘rchild’ pointer points to the right subtree.
Figure 12
A straightforward implementation of this representation can be done by using pointers to implement the links.
Here is the ‘C’ declaration of a binary node:
struct TreeNode
{
struct TreeNode *lchild;
int data;
struct TreeNode *rchild;
};
typedef struct TreeNode TreeNode;
Now we will assume that ‘Root’ will be point to the root node of the tree. If ‘Root’ is Null then the tree is empty. The root node of a binary tree is declared as:
TreeNode *Root;
The linked list representation of the binary tree shown in figure-10 is given in figure-13.
However we can also implement the linked representation using array elements. Using array implementation, we may declare it as:
struct TreeNode
{
int lchild;
int data;
int rchild;
};
struct TreeNode Tree[N];
where ‘N’ specifies the total number of nodes in a binary tree. But unfortunately this array representation suffers from its static image. It means that we can neither shrink nor expand its size during the execution of a program. For the further discussion of binary tree we will consider the linked list representation of binary tree using dynamic nodes (pointers).
Figure 13
Binary Tree Traversals
There are many operations that we want to perform on binary trees. Traversing of a binary tree is one of the most important operations required to be done on a binary tree. Traversing of a binary tree is a process of visiting each node in the tree exactly once. If there are ‘n’ nodes in a binary tree then there are n! different orders in which they could be visited, but most of these have little regulatory or pattern. However a good traversal technique visits the nodes of a tree in some linear sequence. At a given node, then, there are three tasks we shall wish to do in some order: we may visit the node itself, we may traverse its left subtree; and we may traverse its right subtree. If we name these three tasks V, L and R for visiting the node, moving left and moving right respectively, then there are six possible combinations of traversal:
LVR LRV VLR VRL RVL RLV
If it is conventionally assumed that visiting the left subtree always precedes visiting the right subtree, then only three traversal techniques remain, LVR, LRV and VLR, which are known as inorder, postorder and preorder respectively. These names are chosen according to the step at which the given node is visited. With inorder traversal, the node is visited between the subtrees, with postorder it is visited after both the subtrees and with preorder the node is visited before the subtrees.
Actually the choice of the name inorder, posrorder and preorder is not accidental, rather relates closely to producing the infix, postfix, and prefix forms of an expression. A tress representing an expression is known as an expression tree. An expression tree is constituted from the simple operands as the leaf nodes of a binary tree, and the operators as the non-terminal nodes. Figure-14 shows a simple expression tree.
Figure 14
This expression tree consists of binary expression. However for a unary operator, one subtree will be empty. At this stage we will not worry for how this binary tree was formed but assume that it is available. Now we will use this tree to illustrate each of three familiar traversal techniques.
Inorder Traversal
If a binary tree ‘T’ is traversed in an inorder form then we move down the tree towards the left until we can go no farther. Then we visit the node, move one node to the right and continue the same process for the left subtree of right node. However if we can not move to the right, go back one more node.
Here is the algorithm which traverses a binary tree in an inorder fashion.
Algorithm : Inorder
Inorder(Root)
Here ‘Root’ contains the address of the root node.
Perform these steps if Root is not equal to Null.
Step-1 : Call Inorder(Root->lchild)
Step-2 : Print the value of data of Root node
Step-3 : Call Inorder(Root->rchild)
Step-4 : Exit
The ‘C’ implementation of this algorithm is as:
Implementation : Inorder
Inorder(TreeNode *Root)
{
if (Root != NULL)
{
Inorder(Root->lchild);
printf(“\n%c”, Root->data);
Inorder(Root->rchild);
}
}
If this function is executed on the tree of figure-14 then the following output would be printed:
A + B * C / D
Since this traversal gives the infix form of an expression, that’s why it is called as inorder traversal.
Postorder Traversal
If a binary tree ‘T’ is traversed in a postorder form then the left subtree of ‘T’ is traversed first, then the right subtree of ‘T’ is traversed and finally the root node of ‘T’ is visited.
Here is the algorithm which traverses a binary tree in postorder fashion.
Algorithm : Postorder
Postorder(Root)
Here ‘Root’ contains the address of the root node.
Perform these steps if Root is not equal to Null.
Step-1 : Call Postorder(Root->lchild)
Step-2 : Call Postorder(Root->rchild)
Step-3 : Print the value of data of Root node
Step-4 : Exit
The ‘C’ implementation of this algorithm is as:
Implementation : Postorder
Postorder(TreeNode *Root)
{
if (Root != NULL)
{
Postorder(Root->lchild);
Postorder(Root->rchild);
printf(“\n%c”, Root->data);
}
}
If this function is executed on the tree of figure-11.14 then the following output would be printed:
A B C * D / +
Since this traversal gives the postfix form of an expression, that’s why it is called as postorder traversal.
Preorder Traversal
If a binary tree ‘T’ is traversed in a preorder form then root node is visited first, then the left subtree of ‘T’ is traversed first, and then finally the right subtree of ‘T’ is traversed.
Here is the algorithm which traverses a binary tree in preorder fashion.
Algorithm : Preorder
Preorder(Root)
Here ‘Root’ contains the address of the root node.
Perform these steps if Root is not equal to Null.
Step-1 : Print the value of data of Root node
Step-2 : Call Preorder(Root->lchild)
Step-3 : Call Preorder(Root->rchild)
Step-4 : Exit
The ‘C’ implementation of this algorithm is as:
Implementation : Preorder
Preorder(TreeNode *Root)
{
if (Root != NULL)
{
printf(“\n%c”, Root->data);
Preorder(Root->lchild);
Preorder(Root->rchild);
}
}
If this function is executed on the tree of figure-14 then the following output would be printed:
/ + A * B C D
Since this traversal gives the prefix form of an expression, that’s why it is called as preorder traversal.
Non-recursive Traversal of a Binary Tree
We can also implement a non-recursive traversal of a binary tree using a stack. First we consider transformation of the recursive function Inorder() in which we traverse the binary tree in an ‘inorder’ fashion.
Here is the non-recursive version of the inorder traversal.
NonRecInorder(TreeNode *Root)
{
TreeStackNode *s;
Initialize(&s);
while (1)
{
while (Root != NULL)
{
PushNode(&s, Root);
Root = Root->lchild;
}
if (IsEmpty(s))
return;
temp = PopNode(&s);
printf(“%c ”, temp->data;
Root = Root->rchild;
}
}
In this function we have used a linked stack ‘s’ and a few functions, such as Initialize, PushNode, PopNode and IsEmpty, while implementing a linked stack.
Here is the ‘C’ declaration of a node for above linked stack:
struct TreeStackNode
{
struct TreeNode *address;
char data;
struct TreeStackNode *link;
};
typedef struct TreeStackNode TreeStackNode;
The operations to be done on such a stack are defined as:
Initialize(TreeStackNode **s)
{
*s = NULL;
}
PushNode(TreeStackNode **s, TreeNode *Root)
{
TreeStackNode *temp;
temp = getnode();
temp->address = Root;
temp->link = (*s);
}
PopNode(TreeStackNode **s)
{
TreeStackNode *temp;
temp = (*s);
(*s) = (s)->link;
return (temp->address);
}
IsEmpty(TreeStackNode *s)
{
if (s == NULL)
return 1;
else
return 0;
}
TreeStackNode *getnode()
{
TreeStackNode *temp;
temp = (TreeStackNode *)malloc(sizeof(TreeStackNode));
return temp;
}
Similarly we can write non-recursive traversal functions for preorder and postorder. Since a recursive tree traversal techniques also implicitly use a stack, therefore now the question arises – “Is binary tree traversal possible without the use of extra space for a stack? Of course we can have a binary tree traversal algorithm without the use of stack by adding a new ‘parent’ field to each node. The ‘parent’ field is used to contain the address of parent of current node. Using this field we can trace our way back to any root and down again.
Storage Class Specifiers
You should know that before using any variable we have to explicitly or implicitly mention its data type. There is no need to declare constants. Generally a variable can store either in memory or one of the CPU registers. And it is storage class that determines where to store the subsequent variable. The storage class also specifies the scope of the variable. The portion of a program where a variable can be accessed is called as the scope of that variable. The scope of the variable is determined by its place of declaration. If we declare a variable inside a function then this variable can be accessed inside the same function only; such variables are called as local variables. If we declare a variable outside all the functions then this variable can be accessed from anywhere in the program; such variables are called as global variables.
C provides four storage classes:
1. auto Storage Class
2. register Storage Class
3. static Storage Class
4. external Storage Class
The auto Storage Class
The keyword ‘auto’ is used to refer automatic variables. Automatic variable is declared as:
auto data_type variable_name;
If you want to declare i as automatic integer variables, j as automatic float variables and k as automatic character variables then the declaration section looks like this:
auto int i;
auto float j;
auto char k;
automatic variables are stored in memory. The default initial value of a automatic variable is a garbage value. The automatic variables are local to a function in which these variables are defined. It means that automatic variables are created when the function (that defines the auto variables) is called and destroyed when that function terminates. We can also declare an automatic variable at the beginning of a block. It means that each invocation of the statement block in which the auto variable is defined gets a first copy with its own memory space and with reinitialization.
Program – str.cpp illustrates this concept.
// Program – str.cpp
#include
void main()
{
auto int i;
cout << “\nEnter first number =”;
cin >> i;
{
auto int i;
cout << “Enter second number =”;
cin >> i;
{
auto int i;
cout << “Enter third number =”;
cin >> i;
cout << “\nThird I = ” << i;
}
cout << “\nSecond I = ” << i;
}
cout << “\nFirst I = ” << i;
}
The output of this program str is as:
Enter first number = 11
Enter second number = 12
Enter third number = 13
Third = 13
Second = 12
First = 11
In this program the value of I is different for each block because they are defined in different blocks. The life of automatic variable exists till the control remains within block. The value of the variable is lost when the control comes out of the block in which the variable is defined.
You will be surprised to know that if you omit keyword ‘auto’ in the above program, the output will remain same. It means that if storage class of a variable is not mentioned then it is supposed to be auto by default unless the program declares them otherwise. By default the function parameters are auto unless you declare them to be in the register storage class.
The register Storage Class
Register variables are stored inside CPU registers in order to provide fast access. Normally any variable is stored in memory. When a reference is made to access it, it is accessed from memory and then it is stored into CPU register. The keyword ‘register’ is used to refer register variables.
Register variable is declared as:
register data_type variable_name;
Like automatic variables, the default initial value of register variable is garbage value. And like auto variables, the register variables are local to a function. The only difference between an auto variable and a register variable is that you can not take the address of register variable because hardware registers on most computers do not have memory addresses. On the other hand you can take the address of an auto variable.
// Program – str1.cpp
#include
void main()
{
register int i;
cout << “\n Enter any integer value = ”;
cin >> i;
cout << “You have entered = ” << i;
}
The output of this program str1.cpp is as:
Enter any integer value = 10
You have entered =10
While using register storage class, one should remember that if the available registers are not enough, due to any reason, the variables are treated as automatic variables. In other words we can say that when we declare a variable as a register variable then it is a request to the compiler. The compiler can ignore this request. The address restriction applies even when the compiler ignores the request and puts the variable in addressable memory. Another main point to note is that register storage class is restricted to integer variables only because CPU registers are 16 – bit registers & each integer occupies 2 bytes.
Thus the following statement
register float j;
register float k;
would not result in any compiler error. The reason is simple that these variables, j and k, are treated as automatic variables.
The external Storage Class
External variables are also referred as global variables. External variables are declared outside all functions.
An external variable is declared as:
extern data_type variable_name;
Like automatic variables, the external variables are also stored in memory. But unlike automatic variables, the default value of external variable is zero. While using external variables, remember that external variables are not restricted to any function locally. Instead the value of global variable is available to each and every function of the program. Therefore the life of external variable remains present throughout the program and is destroyed only when the program terminates.
Watch carefully the output of the following program:
// Program – str2.cpp
#include
void first();
void second();
int x;
void main()
{
extern int x;
x=15;
cout << "\nFirst X =" << x;
first();
cout << "\nSecond X = " << x;
second();
cout << "\nThird X = " << x;
}
void first()
{
x += 5;
}
void second()
{
x -= 10;
}
The output of this program is as….
X = 15
X = 20
X = 10
An external variable declaration may be inside or outside the function that references it. If the variable is declared outside all functions then every function in the program can reference this external variable. On the other hand if the variable is declared inside a function then only those functions which have the extern declaration of the variable can reference the variable.
Here one main point to note that use of an external outside all the function is optional. That’s why even following program would work correctly.
// Program – str3.cpp
#include
void first();
float y;
void main()
{
y = 20.45;
cout << “\nFirst Y = ” << y;
first();
}
void first()
{
y = y+10.25;
cout << “\nSecond Y = ” << y;
}
The output of this program is as:
Local Y = 27.5
Global Y = 20.25
Thus if there is any conflict between the global and local variable, the local variable wins the race.
The static Storage Class
There are two types of static variables:
1. Static local variables (internal static variable)
2. Static global variables (external static variable)
Static local variables are declared inside a function by using a keyword ‘static’ as:
static datatype variable_name;
A static local variable is initialized only once when the very first call to the function (that defines it) occurs. The value of static local variable would not be destroyed even the function terminates. But remember that the static local variable can be accessed within its own function.
Following program illustrates this concept:
// Program – str4.cpp
#include
void example();
void main()
{
example();
example();
example();
}
void example()
{
static float x = 20.25;
float y = 10.45;
cout << “\n X = “ << x << “\tY = ” << y;
x = x+0.25;
y = y+0.25;
}
The output of this program is as:
X = 20.25 Y = 10.45
X = 20.5 Y = 10.45
X = 20.75 Y = 10.45
In function example(), x is declared as static variable whereas y is declared as automatic variable. Therefore when function example() is called first time, x is initialized to 20.25 once whereas y is initialized each time we call function example().
Like external variable, a static global variable is declared outside any function. But it is slightly different than ordinary external variable. And any file (program) can access ordinary external variable, whereas a static global variable can be only accessed in the file (program) it is defined.
Now let us summarize the different storage classes:
Storage class |
Keyword |
Storage |
Default initial value |
Scope |
Life |
Automatic Storage class |
auto |
memory |
Garbage value |
Local to the block |
temporary |
Register Storage class |
register |
CPU register |
Garbage value |
Local to the block |
temporary |
External Storage class |
extern |
memory |
Zero |
Global to all files |
persistent |
Internal static Storage class |
static |
memory |
Zero |
Local to the block |
persistent |
External Static Storage class |
static |
memory |
Zero |
Global to one file |
persistent |
In this article we will discuss how to define, declare and call C functions. Before writing of large and complex programs, one should remember that our program should be readable, understandable, easily debugged and easily modified. Therefore the idea is to break a large problem into subproblems that can be handled more easily. If the subproblem is still a bit complex, it can be further divided it into smaller subproblems. This process continues until each subproblem can not be further divided. For example – in a magical matrixes problem, one module could read data items into matrix, one could display these data items, one could provides the result of two matrices, other may add diagonal elements of a matrix, and so on. These subproblems are referred to as modules. C programs are not like QBasic or FORTRAN programs. C was designed to force you to think in a modular, or subroutine-like, functional style. Good C programmers write programs that consist of many small functions, even if their programs execute one or more of these functions only once. But those functions work together to produce a program quicker and easier than if the program had to be written from scratch. C is basically a collection of functions in which main() is an essential function. The C functions you have used so far, such as printf and scanf, are built into the C libraries. However, in C you can also write our own functions to organize and simplify large and complex programs. This article describes how to define, declare and call C functions. If your program does very much, break it into several functions. Each function should do one primary task. For example, if you were writing a C program to get a list of characters from the keyboard, alphabetize them, and then print them to the screen, you could—but shouldn't—write all that into one big main() function, as the following C skeleton (program outline) shows: { /* : C code to get a list of characters C code to alphabetize the characters C code to print the alphabetized list on the screen : */ return 0; }
Functions In C Breaking Down Problems
main()
This skeleton is not a good way to write this program. Even though you could type this program in only a few lines of code, it would be much better to get in the habit of breaking up every program into distinct tasks. You should not use main() to do everything—in fact, you should use main() to do little except call each of the functions that do the work.
A better way to organize this program is to write a separate function for each task the program is supposed to do. This doesn't mean that each function should be only one line long. Rather, it means you should make every function a building block that performs only one distinct task in the program.
The following program outline shows you a better way to write the program just described:
main(){
getletters(); /* Calls a function to get the numbers */
alphabetize(); /* Calls a function to alphabetize
letters */
printletters(); /* Calls a function to print letters
onscreen */
return 0; /* Return to the operating system */
}
getletters()
{
/* :C code to get a list of characters: */
return 0; /* Returns to main() */
}
alphabetize()
{
/* C code to alphabetize the characters: /*
return 0; /* Returns to main() */
}
printletters()
{
/* :C code to print the alphabetized list on the screen : */
return 0; /* Returns to main() */
}
The program outline shows you a much better way of writing this program. It is longer to type, but much better organized. The only thing the main() function does is control the other functions by showing in one place the order in which they are called. Each separate function executes its instructions, and then returns to main(), whereupon main() calls the next function until no more functions remain. The main() function then returns control of the computer to the operating system.
The first function called main() is what you previously used to hold the entire program. From this point, in all but the smallest of programs, main() simply controls other functions that do the work.These listings are not examples of real C programs; instead, they are skeletons, or outlines, of programs. From these outlines, it is easier to develop the actual full program. But before going to the keyboard to write a program such as this, you should know that there will be four distinct sections: a primary function-calling main() function, a keyboard data-entry function, an alphabetizing function, and a printing function.
You should never lose sight of the original programming problem and, with the approach just described, you never do. Look again at the main() calling routine in the preceding program. Notice that you can glance at main() and get a feel for the overall program, without the remaining statements getting in the way. This is a good example of structured, modular programming. A large programming problem has been broken down into distinct, separate modules called functions, and each function performs one primary job in a few C statements.
Considering More Function Basics
Little has been said about naming and writing functions, but you probably understand much of the goals of the previous listing already. C functions generally adhere to the following rules:
- Every function must have a name.
- Function names are made up and assigned by the programmer (you!) following the same rules that apply to naming variables; they can contain up to 31 characters, they must begin with a letter, and they can consist of letters, numbers, and the underscore (_) character.
- All function names have one set of parentheses immediately following them. This helps you (and C) differentiate them from variables. The parentheses might or might not contain something.
- The body of each function, starting immediately after the closing parenthesis of the function name, must be enclosed by braces. This means that a block containing one or more statements makes up the body of each function.
Although the outline shown in the previous listing is a good example of structured code, it can be further improved—and not only by putting the actual C statements inside the program to make it work. You can improve this program also by using the underscore character (_) in the function names. Do you see how get_letters() and print_letters() are much easier to read than are getletters() and printletters()? Then again, if you use uppercase letters, you may prefer to capitalize each word in the function name after the initial once, such as getLetters() and printLetters(). The selection of uppercase and lowercase or the underscore in function and variable names depends solely on the programmer's preference
The following listing shows you an example of a C function. You can already tell quite a bit about this function. You know, for instance, that it isn't a complete program because it has no main() function. (All programs must have a main() function.) You know also that the function name is calcIt because parentheses follow this name. These parentheses happen to have something in them. You know also that the body of the function is enclosed in a block of braces. Inside that block is a smaller block, the body of a while loop. Finally, you recognize that the return statement is the last line of the function:
calcIt(int n){
/* Function to print the square of a number */
int square
while (square
{ square = n * n;
printf(''The square of %d is %d \n", n, square);
n++; } /* A block within the function */
return 0;
}
Calling and Returning Functions
A function call in C is like a detour on a highway. Imagine you are traveling along the "road" of the primary function called main() and then run into a function-calling statement. You must temporarily leave the main() function and go execute the function that was called. After that function finishes (its return statement is reached), program control reverts to main(). In other words, when you finish a detour, you end up back on the "main" route and continue the trip. Control continues as main() calls other functions.
Function Calls
A function is called in much the same way it is declared. Any function is called through a function call. A function call is an expression that transfers the control and actual arguments, if any, to a function. The syntax of a typical function call is:
func-name (list-of-actual-arguments);
Here the func-name is the name of the function to be called and the list-of-actual-arguments contains a list of the actual arguments (separated by commas) passed to the function. If the function takes no argument, the list-of-actual-arguments can be empty. The first argument in the list is always correspond to the first formal parameter of the function, the second one corresponds to the second formal parameter and so on through the list.
After the execution of the function call, the control is transferred to the first statement in the function. The control is returned back from the called function to the caller when the called function encounters a return statement. If no return statement is executed, the control is returned back after the execution of the last statement of the called function. In such cases, the return value is undefined.
One main point to note here is that when the called function uses the copies of the actual arguments, any change it makes to the formal arguments do not affect the values of the variables from which the copies may have been made.
Intro:
In this article, I am giving information about recursion, a powerful technique which can be used in place of iteration. Generally recursive solutions are less efficient than iterative solutions to some problems. However the recursive solutions to some problems are so natural that it is very difficult to solve them iteratively. Therefore we can not skip this recursion importance.
Some Definition and Examples:
The concept of recursion is just like a set of Russian dolls which fits inside one another, that is inside the first doll is a smaller doll, inside which is yet a smaller doll…. In other words you can say that the recursion keeps reproducing its smaller and smaller examples of its until a solution is found.
And when we talk about recursion in a high-level language such as C/C++, its definition is as: the ability of a function to call itself within itself. A situation in which a function calls itself is said to be recursive call. The word recursive means “having the characteristics of coming up again and again, or repeating”. In this case, a function call is being repeated by the function itself. Recursion is very powerful feature of C language and is used in more advanced programs. But until you understand how to use recursion effectively, you must not use recursion.
In C language, any function can call any function, even itself. You will be surprised to know that even main () can be called recursively. And there is no limitation on any number of recursive call.
Let us understand the concept of recursion by taking a recursive example. We all know that C does not provide a power operator. So let us firstly write a simple function to calculate Xn (without recursion) where X and n are both non-zero, positive integer.
power(int a, int b)
{
int i, val;
val = 1;
for(i=1; i<=b; i++)
val = val * a;
return (val);
}
Now i am rewriting this power function using recursion. Before writing this recursive function, let me make you understand the arithmetical approach to solve this problem. We know that
If we know Xn-1 we can easily calculate Xn. This definition is a classic recursive definition, that is the definition is given in terms of a smaller version of itself.
Same way
If we know the value of Xn-2 we can calculate Xn-1 and thus calculate Xn.
Xn = X(X(Xn-2)
And this process will continue until the innermost expression becomes X0. We know what X0 is, it is 1. Whenever we use recursion, we should remember the answer without resorting to a recursive definition. The case for which an answer is explicitly known is called as base case and the case for which the solution is expressed in terms of a smaller version of itself is called the recursive case or general case. In this example, “in terms of smaller version of itself” means that the exponent is decremented each time. Thus we can say that the base case is the case for which the solution can be stated non-recursively.
Now it is easy to write the recursive function for Xn..
power(int x, int n)
{
int val ;
if (n==0 ) /* base case */
return (1) ;
else
{
val = x * power(x, n-1) ; /*recursive case */
return (val) ;
}
}
Now trace the execution of this recursive function with x equal to 2 and n equal to 4. During each call the power() function passes the actual parameters to the version being called. The value of ‘x’ is same for each power function call, but the value of ‘n’ is decremented by 1 for each call until ‘n’ becomes 1. When ‘n’ becomes 0, the power() function starts returning a value, which is 1 in our example. This value of power() function is passed back to the previous version that made this call and this process of returning values will continue until the value of the power can be passed to the original call.
see what happens during each call.
Call-1 – power() is called with ‘x’ equal to 2 and ‘n’ equal to 4. Since ‘n’ is not equal to 0, so power() is called with ‘x’ and ‘n-1’ as parameters. Execution of the call to the function halts until the result is sent back from this recursive call.
Call-2 – power() is called with ‘x’ equal to 2 and ‘n’ equal to 3. Since ‘n’ is not equal to 0, so power() is called with ‘x’ and ‘n-1’ as parameters. Execution of the call to the function halts until the result is sent back from this recursive call.
Call-3 – power() is called with ‘x’ equal to 2 and ‘n’ equal to 2. Since ‘n’ is not equal to 0, so power() is called with ‘x’ and ‘n-1’ as parameters. Execution of the call to the function halts until the result is sent back from this recursive call.
Call-4 – power() is called with ‘x’ equal to 2 and ‘n’ equal to 1. Since ‘n’ is not equal to 0 now, so power() is called with ‘x’ and ‘n-1’ as parameters. Execution of the call to the function halts until the result is sent back from this recursive call.
Call-5 – power() is called with ‘x’ equal to 2 and ‘n’ equal to 0. Since ‘n’ is now equal to 0, the value of ‘x’ is stored in power(2, 0). Fortunately this call to the function has finished execution, and power() is passed back to the place in the statement from which the call was made.
Call-4 – this call to the function power() is now complete. The value ‘1’ is returned and power() passes this value back to the place in the statement from which the call was made.
Call-3 – this call to the function power() is now complete. This value (which is 2) is multiplied by ‘x’ and power() is passed back to the place in the statement from which the call was made.
Call-2 – this call to the function power() is now complete. This value (which is 4) is multiplied by ‘x’ and power() is passed back to the place in the statement from which the call was made.
Call-1 – this call to the function power() is now complete. This value (which is 8) is multiplied by ‘x’ and power() is passed back to the place in the statement from which the call was made. Since the first call has now been completed, that is the final value of the function power().
Figure-1 the function of power with the block diagrams. Each block calls the power function with the value of actual parameters.
Initially we call power function as:
Figure 1
If you find this example not crystal clear then let us study another recursive function, which calculates the factorial of a number. If we have a positive integer ‘n’, then ‘n’ factorial is defined as the product of all integers between ‘n’ and 1. For example, 4 factorial equals 4*3*2*1 = 24. The factorial of 0 is defined as 1. Generally the exclamation mark (!) is often used to denote the factorial function. Thus the factorial of a number ‘n’ is written as:
n! = n * (n-1) * (n-2) * (n-3) * .... *3 * 2 * 1
Or you can say
n! = n * (n-1)!
Now this expression looks like a recursive definition. (n-1)! Is a smaller instance of n!. Same way
n! = n * ((n-1 ) * (n-2)!)
And this process will continue until n becomes zero and 0! is defined as 1. Thus when ‘n’ is equal to 0, we can set the result to 1.
Here is the function that implements this task.
factorial(int n)
{
if ( n == 0 )
return (1) ;
else
return (n * factorial (n-1)) ;
}
Now let us look at the execution of this program by assuming ‘n’ to be 5.
Call-1 – factorial() is called with ‘n’ equal to 5. Since n is not 0, the else statements will be executed. The very first statement in else block is using a recursive call with (n-1) which is 4, so the assignment will not be completed and hence no value is returned.
Call-2 – factorial() is called with ‘n’ equal to 4. Since n is not 0, the else statements will be executed. The very first statement in else block is using a recursive call with (n-1) which is 3, so the assignment will not be completed and hence no value is returned.
Call-3 – factorial() is called with ‘n’ equal to 3. Since n is not 0, the else statements will be executed. The very first statement in else block is using a recursive call with (n-1) which is 2, so the assignment will not be completed and hence no value is returned.
Call-4 – factorial() is called with ‘n’ equal to 2. Since n is not 0, the else statements will be executed. The very first statement in else block is using a recursive call with (n-1) which is 1, so the assignment will not be completed and hence no value is returned.
Call-5 – factorial() is called with ‘n’ equal to 1. Since n is not 0, the else statements will be executed. The very first statement in else block is using a recursive call with (n-1) which is 0, so the assignment will not be completed and hence no value is returned.
Call-6 – factorial() is called with ‘n’ equal to 0. Since n is equal to 0. This call to the function factorial() has finished executing. The result 1 is sent back to the call 5.
Call-5 – the assignment statement in this copy can now be computed. This call has now completed and returns the value 1.
Call-4 – the assignment statement in this copy can now be computed. This call has now completed and returns the value 2.
Call-3 – the assignment statement in this copy can now be computed. This call has now completed and returns the value 6.
Call-2 – the assignment statement in this copy can now be computed. This call has now completed and returns the value 24.
Call-1 – the assignment statement in this copy can now be computed. This call has now completed and returns the value 120. Since the first call has now been completed, that is the final value of the function factorial().
Figure-2 summarizes this execution process as :
Figure 2
Now briefly write down the ideas before writing a recursive algorithms:
- understand the problem clearly
- determines the base case(s)
- determines the recursive case(s)
No doubt the iteration solutions to above these two recursive problems are simpler and much more efficient. So please do not puzzle. Take a more complicated problem – one in which the recursive solution is not immediately apparent.
Now write a function, which converts a decimal integer (base 10) into a binary integer (base 2).
Before writing the function directly, let us understand how does this conversion take place? A decimal number (base 10) is converted into a binary number (base 2) as follows:
- take the decimal number ‘n’ and divide it by two
- write the remainder the rightmost digit in the output
- replace the original number with the quotient
- repeat, placing each new remainder to the left of the previous remainder
- Step (iii) and (iv) will continue until the quotient is zero.
we want to convert a decimal 34 into binary,
34 / 2 = 17
34 % 2 = 0 print remainder as: 0
17 / 2 = 8
17 % 2 = 1 place the remainder to left of the previous one as: 10
8 / 2 = 4
8 %2 = 0 place the remainder to left of the previous one as: 010
4 / 2 = 2
4 % 2 = 0 place the remainder to left of the previous one as: 0010
2 / 2 = 1
2 % 2 = 0 place the remainder to left of the previous one as: 00010
1 / 2 = 0
1 % 2 = 1 place the remainder to left of the previous one as: 100010
This is clearly a by hand algorithm for a calculator and paper. Expression such as “to the left of” are certainly not implemented in C as yet. One may think that the problem could be implemented with a straight forward iterative algorithm as follows:
binary(int n)
{
if (n == 0 )
{
printf(“%d”,n);
return;
}
else
while ( n != 0 )
{
printf("%d",(n%2));
n = n /2;
}
return ;
}
When we use this function with the decimal integer number 34 we will get the output: 010001
This answer is just the reverse of the expected output. In this first remainder is printed firstly, then second, third and so on. But in reality we need to print the last remainder first, then second last remainder, third last remainder and in last we should print the first remainder. This is just like a recursive definition in which the last call returns the value first. We can summarized by saying that for any given number, n, it should print n%2 after (n/2)%2 has been printed.
Here is the recursive algorithm that converts a decimal integer number into a binary number.
binary(int n)
{
if (n == 0 )
return ;
else
{
binary(n/2);
printf("%d",(n%2));
}
}
Now when we use this function with the decimal integer 34 we obtain the output: 100010.
Introduction to C Functions
From this article you can take advantage of your computer's repetitive nature by looking at your programs in a new way—as a series of small routines that execute whenever you need them, however many times you require. This article approaches its subject a little differently from previous articles. It concentrates on teaching the need for writing your own functions, which are modules of code that you execute and control from the main() function. So far, all programs in this article have consisted of a single long function called main().
This article stresses the use of structured programming, sometimes called modular programming. C was designed to make it easy to write your programs in several modules instead of as one long program. By breaking the program into several smaller routines (functions), you can isolate problems, write correct programs faster, and produce programs that are easier to maintain.
This article make you understand the following basics:
• The need for functions
• How to trace functions
• How to write functions
• How to call and return from functions
Function Basics
When you approach an application that needs to be programmed, it is best not to sit down at the keyboard and start typing. Rather, you should first think about the program and what it is supposed to do. One of the best ways to attack a program is to start with the overall goal, and then divide this goal into several smaller tasks. You should never lose sight of the overall goal, but you should think also of how individual pieces can fit together to accomplish such a goal.
When you finally do sit down to begin coding the problem, continue to think in terms of those pieces fitting together. Don't approach a program as if it were one giant problem; rather, continue to write those small pieces individually.
This does not mean that you must write separate programs to do everything. You can keep individual pieces of the overall program together—if you know how to write functions. This way, you can use the same functions in many different programs.
C programs are not like QBasic or FORTRAN programs. C was designed to force you to think in a modular, or subroutine-like, functional style. Good C programmers write programs that consist of many small functions, even if their programs execute one or more of these functions only once. But those functions work together to produce a program quicker and easier than if the program had to be written from scratch.
Rather than code one very long program, you should write several smaller routines, called functions. One of those functions must be called main(). The main() function is always the first to execute. It doesn't need to be first in a program, but it usually is.
Breaking Down Problems
If your program does very much, break it into several functions. Each function should do one primary task. For example, if you were writing a C program to get a list of characters from the keyboard, alphabetize them, and then print them to the screen, you could—but shouldn't—write all that into one big main() function, as the following C skeleton (program outline) shows:
main()
{
/*
C code to get a list of characters
C code to alphabetize the characters
C code to print the alphabetized list on the screen
*/
return 0;
}
This skeleton is not a good way to write this program. Even though you could type this program in only a few lines of code, it would be much better to get in the habit of breaking up every program into distinct tasks. You should not use main() to do everything—in fact, you should use main() to do little except call each of the functions that do the work.
A better way to organize this program is to write a separate function for each task the program is supposed to do. This doesn't mean that each function should be only one line long. Rather, it means you should make every function a building block that performs only one distinct task in the program.
The following program outline shows you a better way to write the program just described:
main()
{
getletters(); /* Calls a function to get the numbers */
alphabetize(); /* Calls a function to alphabetize
letters */
printletters(); /* Calls a function to print letters
onscreen */
return 0; /* Return to the operating system */
}
getletters()
{
/* :
C code to get a list of characters
: */
return 0; /* Returns to main() */
}
alphabetize()
{
/* :
C code to alphabetize the characters
: /*
return 0; /* Returns to main() */
}
printletters()
{
/* :
C code to print the alphabetized list on the screen
: */
return 0; /* Returns to main() */
}
The program outline shows you a much better way of writing this program. It is longer to type, but much better organized. The only thing the main() function does is control the other functions by showing in one place the order in which they are called. Each separate function executes its instructions, and then returns to main(), whereupon main() calls the next function until no more functions remain. The main() function then returns control of the computer to the operating system.
The first function called main() is what you previously used to hold the entire program. From this point, in all but the smallest of programs, main() simply controls other functions that do the work.
These listings are not examples of real C programs; instead, they are skeletons, or outlines, of programs. From these outlines, it is easier to develop the actual full program. But before going to the keyboard to write a program such as this, you should know that there will be four distinct sections: a primary function-calling main() function, a keyboard data-entry function, an alphabetizing function, and a printing function.
You should never lose sight of the original programming problem and, with the approach just described, you never do. Look again at the main() calling routine in the preceding program. Notice that you can glance at main() and get a feel for the overall program, without the remaining statements getting in the way. This is a good example of structured, modular programming. A large programming problem has been broken down into distinct, separate modules called functions, and each function performs one primary job in a few C statements.
Considering More Function Basics
Little has been said about naming and writing functions, but you probably understand much of the goals of the previous listing already. C functions generally adhere to the following rules:
1. Every function must have a name.
2. Function names are made up and assigned by the programmer (you!) following the same rules that apply to naming variables; they can contain up to 31 characters, they must begin with a letter, and they can consist of letters, numbers, and the underscore (_) character.
3. All function names have one set of parentheses immediately following them. This helps you (and C) differentiate them from variables. The parentheses might or might not contain something. So far, all such parentheses in this article have been empty.
4. The body of each function, starting immediately after the closing parenthesis of the function name, must be enclosed by braces. This means that a block containing one or more statements makes up the body of each function.
Although the outline shown in the previous listing is a good example of structured code, it can be further improved—and not only by putting the actual C statements inside the program to make it work. You can improve this program also by using the underscore character (_) in the function names. Do you see how get_letters() and print_letters() are much easier to read than are getletters() and printletters()? Then again, if you use uppercase letters, you may prefer to capitalize each word in the function name after the initial once, such as getLetters() and printLetters(). The selection of uppercase and lowercase or the underscore in function and variable names depends solely on the programmer's preference.
The following listing shows you an example of a C function. You can already tell quite a bit about this function. You know, for instance, that it isn't a complete program because it has no main() function. (All programs must have a main() function.) You know also that the function name is calcIt because parentheses follow this name. These parentheses happen to have something in them. You know also that the body of the function is enclosed in a block of braces. Inside that block is a smaller block, the body of a while loop. Finally, you recognize that the return statement is the last line of the function:
calcIt(int n)
{
/* Function to print the square of a number */
int square;
while (square
{ square = n * n;
printf(''The square of %d is %d \n", n, square);
n++; } /* A block within the function */
return 0;
}
Calling and Returning Functions
I think till now You have been reading much about "function calling" and "returning control." in this article. Although you may already understand these phrases from their context, you can probably learn them better through an illustration of what is meant by a function call.
A function call in C is like a detour on a highway. Imagine you are traveling along the "road" of the primary function called main() and then run into a function-calling statement. You must temporarily leave the main() function and go execute the function that was called. After that function finishes (its return statement is reached), program control reverts to main(). In other words, when you finish a detour, you end up back on the "main" route and continue the trip. Control continues as main() calls other functions.
Example
A complete C program, with functions, should make this clear. The following program prints several messages to the screen. Each message printed is determined by the order of the functions. Before worrying too much about what this program does, take a little time to study its structure. You should be able to see three functions defined in the program: main(), nextFun(), and thirdFun(). A fourth function is used also, but it is the built-in C printf() function. The three defined functions appear sequentially, one after the other. The body of each is enclosed in braces, and each has a return statement at its end:
/* FUN1.C
The following program illustrates function calls */
#include
main() /* main() is always the first C function executed */
{
printf(''First function called main() \n");
nextFun(); /* Second function is called here */
thirdFun(); /* This function is called here */
printf("main() is completed \n"); /* All control
returns here */
return 0; /* Control is returned to
the operating system */
} /* This brace concludes main() */
nextFun() /* Second function;
parentheses always required */
{
printf("Inside nextFun() \n"); /* No variables are
defined in the program */
return 0; /* Control is now returned to main() */
}
thirdFun() /* Last function in the program */
{
printf("Inside thirdFun() \n");
return 0; /* Always return from all functions*/
Output
The output of this program follows:
First function called main()
Inside nextFun()
Inside thirdFun()
main() is completed
Figure 1 shows a tracing of this program's execution. Notice that main() controls which of the other functions is called, as well as the order of the calling. Control always returns to the calling function after the called function finishes.
Figure 1: Use this figure to follow this function call trace.
To call a function, simply type its name—including the parentheses—and follow it with a semicolon. Remember that semicolons follow all executable statements in C, and a function call (sometimes called a function invocation) is an executable statement. The execution is the function's code being called. Any function can call any other function. It just happens that main() is the only function that calls other functions in this program.
Now, you can tell that the following statement is a function call:
printTotal();
Because printTotal is not a C command or built-in function name, it must be a variable or a written function's name. Only function names end with the parentheses, so it must be a function call or the start of a function's code. Of the last two possibilities, it must be a call to a function because it ends with a semicolon. If it didn't have a semicolon, it would have to be the start of a function definition.
When you define a function (that is, when you type the function name and its subsequent code inside braces), you never follow the name with a semicolon. Notice in the previous program that main(), nextFun(), and thirdFun() have no semicolons when they appear in the body of the program. A semicolon follows their names only in main(), where these functions are called.
Example
Suppose you are writing a program that does the following. First, it asks users for their departments. Next, if they are in accounting, they should receive the accounting department's report. If they are in engineering, they should receive the engineering department's report. And if they are in marketing, they should receive the marketing department's report.
The skeleton of such a program follows. The code for main() is shown in its entirety, but only a skeleton of the other functions are shown. The switch statement is a perfect function-calling statement for such multiple-choice selections:
/* Skeleton of a departmental report program */
#include
main()
{
int choice;
do
{ printf(''Choose your department from the
following list\n");
printf("\t1. Accounting \n");
printf("\t2. Engineering \n");
printf("\t3. Marketing \n");
printf("What is your choice? ");
scanf(" %d", &choice);
} while ((choice3)); /* Ensure 1, 2,
or 3 */
switch choice
{ case(1): { acctReport(); /* Call accounting function */
break; } /* Don't fall through */
case(2): { engReport(); /* Call engineering function */
break; }
case(3): { mtgReport(); /* Call marketing function */
break; }
}
return 0; /* Program returns to the operating system
when finished */
}
acctReport()
{
/* :
Accounting report code goes here */
: */
return 0;
}
engReport()
{
/* :
Engineering report code goes here */
: */
return 0;
}
mtgReport()
{
/* :
Marketing report code goes here */
: */
return 0;
}
The bodies of switch statements normally contain function calls. You can tell that these case statements execute functions. For instance, acctReport(); (which is the first line of the first case) is not a variable name or a C command. It is the name of a function defined later in the program. If users enter 1 at the menu, the function called acctReport() executes. When it finishes, control returns to the first case body, whose break statement causes the switch statement to end. The main() function returns to DOS (or to your integrated C environment if you are using one) when its return statement executes.
More Articles …
Page 9 of 21