Download as pdf or txt
Download as pdf or txt
You are on page 1of 66

UNIT 4-FUNCTION

Function declaration and prototypes - Parameter


passing – Recursion - Command line arguments
Function pointers - Passing pointers to functions -
Passing arrays to functions - Passing function to
other functions. Storage classes-C preprocessor.
INTRODUCTION

• C enables its programmers to break up a program into


segments commonly known as functions, each of which can
be written more or less independently of the others.
• Every function in the program is supposed to perform a well
defined task. Therefore, the program code of one function is
completely insulated from that of other functions.
• Every function has a name which acts as an interface to the
outside world in terms of how information is transferred to it
and how results generated by the function are transmitted
back from it.
• main() calls another function, func1() to perform a well defined task.
• main() is known as the calling function and func1() is known as the
called function.
• When the compiler encounters a function call, instead of executing the
next statement in the calling function, the control jumps to the
statements that are a part of the called function.
• After the called function is executed, the control is returned back to the
calling program.
INTRODUCTION CONTD….
• It is not necessary that the main() can call only one function, it can call as many functions as it wants
and as many times as it wants. For example, a function call placed within a for loop, while loop or
do-while loop may call the same function multiple times until the condition holds true.
• It is not that only the main() can call another functions. Any function can call any other function. In
the fig. one function calls another, and the other function in turn calls some other function.

main() func1() func2() func3()


{ { { {
………….. ………..
………….. ………… ……….. ………..
func1(); func2();
………… ……….. ………… …………
……….. ……….
return 0; return; func3(); ………..
} } ……….
……….. return;
………. }
return;
}
WHY DO WE NEED FUNCTIONS?
• Dividing the program into separate well defined functions facilitates each
function to be written and tested separately. This simplifies the process of
getting the total program to work.
• Understanding, coding and testing multiple separate functions are far easier
than doing the same for one huge function.
• If a big program has to be developed without the use of any function (except
main()), then there will be countless lines in the main() .
• All the libraries in C contain a set of functions that the programmers are free
to use in their programs. These functions have been prewritten and
pre-tested, so the programmers use them without worrying about their code
details. This speeds up program development.
TERMINOLOGY OF FUNCTIONS

• A function, f that uses another function g, is known as the calling


function and g is known as the called function.
• The inputs that the function takes are known as arguments
• When a called function returns some result back to the calling function,
it is said to return that result.
• The calling function may or may not pass parameters to the called
function. If the called function accepts arguments, the calling function
will pass parameters, else not.
• Main() is the function that is called by the operating system and
therefore, it is supposed to return the result of its processing to the
operating system.
FUNCTION DECLARATION
• Function declaration is a declaration statement that identifies a
function with its name, a list of arguments that it accepts and the type
of data it returns.
• The general format for declaring a function that accepts some
arguments and returns some value as result can be given as:
return_data_type function_name(data_type variable1, data_type
variable2,..);
• No function can be declared within the body of another function.
Example, float avg ( int a, int b);
FUNCTION DEFINITION

• Function definition consists of a function header that identifies the function, followed by
the body of the function containing the executable code for that function
• When a function defined, space is allocated for that function in the memory.
• The syntax of a function definition can be given as:
return_data_type function_name(data_type variable1, data_type variable2,..)
{
………….
statements
………….
return( variable);
}
• The no. and the order of arguments in the function header must be same as that given in
function declaration statement.
FUNCTION CALL
• The function call statement invokes the function.
• When a function is invoked the compiler jumps to the called
function to execute the statements that are a part of that
function.
• Once the called function is executed, the program control
passes back to the calling function.
• Function call statement has the following syntax.
function_name(variable1, variable2, …);
Points to remember while calling the function:

• Function name and the number and type of arguments in the function call must be same as
that given in the function declaration and function header of the function definition
• Names (and not the types) of variables in function declaration, function call and header of
function definition may vary
• Arguments may be passed in the form of expressions to the called function. In such a case,
arguments are first evaluated and converted to the type of formal parameter and then the
body of the function gets executed.
• If the return type of the function is not void, then the value returned by the called function
may be assigned to some variable as given below.
variable_name = function_name(variable1, variable2, …);
PROGRAM THAT USES FUNCTION
#include<stdio.h>

int sum(int a, int b); // FUNCTION DECLARATION

int main()

int num1, num2, total = 0;

printf(“\n Enter the first number : “);

scanf(“%d”, &num1);

printf(“\n Enter the second number : “);

scanf(“%d”, &num2);

total = sum(num1, num2); // FUNCTION CALL

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

return 0;

// FUNCTION DEFNITION

int sum ( int a, int b) // FUNCTION HEADER

{ // FUNCTION BODY

return (a + b);

}
RETURN STATEMENT
• The return statement is used to terminate the execution of a function and return control to the calling
function. When the return statement is encountered, the program execution resumes in the calling function
at the point immediately following the function call.

• Programming Tip: It is an error to use a return statement in a function that has void as its return type.

• A return statement may or may not return a value to the calling function. The syntax of return statement
can be given as

return <expression> ;

• Here expression is placed in between angular brackets because specifying an expression is optional. The
value of expression, if present, is returned to the calling function. However, in case expression is omitted,
the return value of the function is undefined.

• Programmer may or may not place the expression within parentheses.

• By default, the return type of a function is int.

• For functions that has no return statement, the control automatically returns to the calling function after

the last statement of the called function is executed.


PASSING PARAMETERS TO THE FUNCTION

• There are two ways in which arguments or parameters can be passed to the called function.
• Call by value in which values of the variables are passed by the calling function to the called function.

• Call by reference in which address of the variables are passed by the calling function to the called function.

Passing parameters to function

Call by value Call by reference


CALL BY VALUE
• In the Call by Value method, the called function creates new variables to store the value of the arguments passed to
it. Therefore, the called function uses a copy of the actual arguments to perform its intended task.
• If the called function is supposed to modify the value of the parameters passed to it, then the change will be
reflected only in the called function. In the calling function no change will be made to the value of the variables.
• #include<stdio.h>
• void add( int n);
• int main()
• {
• int num = 2;
• printf("\n The value of num before calling the function = %d", num);
• add(num);
• printf("\n The value of num after calling the function = %d", num);
• return 0;
• }
• void add(int n)
• {
• n = n * 10;
• printf("\n The value of num in the called function = %d", n);
• }
• The output of this program is:
• The value of num before calling the function = 2
• The value of num in the called function = 20
• The value of num after calling the function = 2
CALL BY REFERENCE
• When the calling function passes arguments to the called function using call by value method, the only way
to return the modified value of the argument to the caller is explicitly using the return statement. The better
option when a function can modify the value of the argument is to pass arguments using call by reference
technique.

• In call by reference, we declare the function parameters as references rather than normal variables. When
this is done any changes made by the function to the arguments it received are visible by the calling
program.

• To indicate that an argument is passed using call by reference, an ampersand sign (&) is placed after the
type in the parameter list. This way, changes made to that parameter in the called function body will then
be reflected in its value in the calling program.
PROGRAM ILLUSTRATING CALL BY REFERENCE
TECHNIQUE
• #include<stdio.h>
• void add( int *n);
• int main()
• {
• int num = 2;
• printf("\n The value of num before calling the function = %d", num);
• add(&num);
• printf("\n The value of num after calling the function = %d", num);
• return 0;
• }
• void add( int *n)
• {
• *n = *n + 10;
• printf("\n The value of num in the called function = %d", n);
• }
• The output of this program is:
• The value of num before calling the function = 2
• The value of num in the called function = 12
• The value of num after calling the function = 12
RECURSIVE FUNCTIONS
• A recursive function is a function that calls itself to solve a smaller version of its task
until a final call is made which does not require a call to itself.
• Every recursive solution has two major cases, they are
base case, in which the problem is simple enough to be solved directly without
making any further calls to the same function
recursive case, in which first the problem at hand is divided into simpler sub parts.
Second the function calls itself but with sub parts of the problem obtained in the
first step. Third, the result is obtained by combining the solutions of simpler
sub-parts.
FINDING FACTORIAL OF A NUMBER USING RECURSION

PROBLEM SOLUTION
5! 5 X 4 X 3 X 2 X 1!
= 5 X 4! = 5 X 4 X 3 X 2 X 1
= 5 X 4 X 3! = 5 X 4 X 3 X 2
= 5 X 4 X 3 X 2! = 5 X 4 X 6
= 5 X 4 X 3 X 2 X 1! = 5 X 24
= 120
Base case is when n=1, because if n = 1, the result is known to be 1
Recursive case of the factorial function will call itself but with a smaller value of n, this case can
be given as
factorial(n) = n X factorial (n-1)
#include<stdio.h>
int Fact(int)
{ if(n==1)
retrun 1;
return (n * Fact(n-1));
}
main()
{ int num;
scanf(“%d”, &num);
printf(“\n Factorial of %d = %d”, num, Fact(num));
return 0;
}
THE FIBONACCI SERIES
• The Fibonacci series can be given as:
0 1 1 2 3 5 813 21 34 55……
• That is, the third term of the series is the sum of the first and
second terms. On similar grounds, fourth term is the sum of
second and third terms, so on and so forth. Now we will design a
recursive solution to find the nth term of the Fibonacci series. The
general formula to do so can be given as
TYPES OF RECURSION
• Any recursive function can be characterized based on: whether the function calls itself directly or
indirectly (direct or indirect recursion). whether any operation is pending at each recursive call
(tail-recursive or not). the structure of the calling pattern (linear or tree-recursive).

Recursion

Direct Indirect Linear Tree Tail

DIRECT RECURSION
A function is said to be directly recursive if it explicitly calls itself. For example, consider the function
given below.
int Func( int n)
{
if(n==0)
retrun n;
return (Func(n-1));
INDIRECT RECURSION
A function is said to be indirectly recursive if it contains a call to another function which ultimately calls
it. Look at the functions given below. These two functions are indirectly recursive as they both call each
other. int Func1(int n) int Func2(int x)
{ {
if(n==0) return Func1(x-1);
return n; }
return Func2(n);
}

TAIL RECURSION
• A recursive function is said to be tail recursive if no operations are pending to be performed when
the recursive function returns to its caller.
• That is, when the called function returns, the returned value is immediately returned from the callin
function.
• Tail recursive functions are highly desirable because they are much more efficient to use as in the
case, the amount of information
int Fact(n)
{
that has to be int
stored
{
on the system stack is independent of the
Fact1(int n, int res)

return Fact1(n, 1); if (n==1)


number of recursive calls.
} return res;
return Fact1(n-1, n*res);
}
LINEAR AND TREE RECURSION
• Recursive functions can also be characterized depending on the way in which the recursion grows- in a
linear fashion or forming a tree structure.
• In simple words, a recursive function is said to be linearly recursive when no pending operation involves
another recursive call to the function. For example, the factorial function is linearly recursive as the
pending operation involves only multiplication to be performed and does not involve another call to
Fact.
• On the contrary, a recursive function is said to be tree recursive (or non-linearly recursive) if the pending
operation makes another recursive call to the function. For example, the Fibonacci function Fib in which
int Fibonacci(int num)
the pending{ operations recursively calls the Fib function.
if(num <= 2)
return 1;
return ( Fibonacci (num - 1) + Fibonacci(num – 2));
}
PROS AND CONS OF RECURSION
• Pros: Recursive solutions often tend to be shorter and simpler than non-recursive ones.
• Code is clearer and easier to use
• Recursion represents like the original formula to solve a problem.
• Follows a divide and conquer technique to solve problems
• In some (limited) instances, recursion may be more efficient
• Cons: For some programmers and readers, recursion is a difficult concept.
• Recursion is implemented using system stack. If the stack space on the system is limited, recursion to a
deeper level will be difficult to implement.
• Aborting a recursive process in midstream is slow and sometimes nasty.
• Using a recursive function takes more memory and time to execute as compared to its non-recursive
counter part.
• It is difficult to find bugs, particularly when using global variables
TOWER OF HANOI
• Tower of Hanoi is one of the main applications of a recursion. It says, "if you can solve n-1 cases, then
you can easily solve the nth case?"

A A B C
B C

If there is only one ring, then simply move the ring from source to the destination

A B C A B C
A B C

If there are two rings, then first move ring 1 to the spare pole
and then move ring 2 from source to the destination. Finally
A B C
move ring 1 from the source to the destination
• Consider the working with three rings.

A B C
A B C
A B C

A B C A B C
A B C

A B C A B C
COMMAND LINE ARGUMENTS
• Command-line arguments are given after the name of a program in command-line operating systems
like DOS or Linux, and are passed in to the program from the operating system.
• The main() can accept two arguments,
• First argument is an integer value that specifies number of command line arguments
• Second argument is a full list of all of the command line arguments.
• The full declaration of main() can be given as,
int main (int argc, char *argv[])
• The integer, argc specifies the number of arguments passed into the program from the command line,
including the name of the program.
• The array of character pointers, argv contains the list of all the arguments. argv[0] is the name of the
program, or an empty string if the name is not available. argv[1] to argv[argc – 1] specifies the command
line argument. In the C program, every element in the argv can be used as a string.
int main(int argc, char *argv[])
{ int i;
printf("\n Number of argumnets passed = %d",argc);
for (i=0; i<argc; i++)
printf("\n arg[i] = %s",argv[i]);
}
For example when we execute the below program from
DoS prompt by writing,

C:\> tc clpro.c sample file

Then argc=3, where argv[0]=clpro.c


argv[1]=sample
argv[2]=file
POINTER TO FUNCTION
• This is a useful technique for passing a function as an argument to
another function.
• In order to declare a pointer to a function we have to declare it like the
prototype of the function except that the name of the function is
enclosed between parentheses () and an asterisk (*) is inserted before
the name.
/* pointer to function returning int */
int (*func)(int a, float b);
• If we have declared a pointer to the function, then that pointer can be
assigned to the address of the right sort of function just by using its
name.
• When a pointer to a function is declared, it can be called using one of
two forms:
(*func)(1,2); ORfunc(1,2);
#include <stdio.h>
void print(int n);
main()
{
void (*fp)(int);
fp = print;
(*fp)(10);
fp(20);
return 0;
}
void print(int value)
{ printf("\n %d", value);
}
Comparing Function Pointers
if(fp >0) // check if initialized
{ if(fp == print)
printf("\n Pointer points to Print");
else
printf("\n Pointer not initialized!");
}
Passing arrays to a function

// Program to calculate the sum of array elements by


passing to a function float calculateSum(float num[]) {
float sum = 0.0;
#include <stdio.h>
float calculateSum(float num[]); for (int i = 0; i < 6; ++i) {
sum += num[i];
int main() { }
float result, num[] = {23.4, 55, 22.6, 3, 40.5, 18};
return sum;
// num array is passed to calculateSum() }
result = calculateSum(num);
printf("Result = %.2f", result);
return 0;
}
Passing function to another function

// main function
#include <stdio.h> int main()
{
// function 1 // define some variables
int fun_1(int a , int b) int i=5 , j=6;
{
return (a+b); // calling the function fun_2 and Passing
} // fun_1 as an argument to it
fun_2(fun_1(i,j));
// function 2
int fun_2(int temp) return 0;
{ }
printf("\nThe value of temp is : %d",temp);
}
POINTERS TO POINTERS

• You can use pointers that point to pointers. The pointers in turn, point to data (or even to other pointers).
To declare pointers to pointers just add an asterisk (*) for each level of reference.

For example, if we have:

int x=10;

int *px, **ppx;

px=&x;
10 1002 2004
ppx=&px;

Now if we write, x px ppx


printf(“\n %d”, **ppx);

Then it would print 10, the value of x.


Storage Classes Types
C Supports four storage classes,
automatic
register
external
static
Storage Classes Types
STORAGE CLASS
FEATURE
Auto Extern Register Static
Local: Accessible
within the function
Accessible within Accessible within or block in which it
Accessible within the
the function or block all program files is declared
Accessibility function or block in
in which it is that are a part of Global: Accessible
which it is declared
declared the program within the program
in which it is
declared
Storage Main Memory Main Memory CPU Register Main Memory
Exists when the Exists when the
function or block in function or block in Local: Retains
which it is declared which it is declared value between
Exists
is entered. Ceases to is entered. Ceases to function calls or
throughout the
Existence exist when the exist when the block entries
execution of the
control returns from control returns from Global: Preserves
program
the function or the the function or the value in program
block in which it block in which it was files
was declared declared

Default value Garbage Zero Garbage Zero


Storage Classes Types Contd…
Example: ‘auto’ Storage Class
#include<stdio.h>

void func1() //function 1 definition


{
int a=10; Output:
printf(“\n a = %d”, a); // auto integer local to func1()
} a = 10
void func2() //function 2 definition a = 20
{ a = 30
int a=20;
printf(“\n a = %d”, a); // auto integer local to func1()
}

void main()
{
int a=30;
func1(); //function 1 call
func2(); //function 2 call
printf(“\n a = %d”, a);
}
Storage Classes Types Contd…
Example: ‘extern’ Storage Class

// FILE 1.C // FILE2.C


#include<stdio.h> #include<stdio.h>
#include<FILE2.C> extern int x;
int x; void print()
void print(void); {
int main() printf("\n X in File2= %d", x);
{ }
x = 10; main()
print(“X in File1=%d”, x); {
print(); // Statements
return 0; }
} // END OF FILE2.C
// END OF FILE1.C
Storage Classes Types Contd…
Example: ‘register’ Storage Class
#include<stdio.h>
int exp(int a, int b); //function declaration

int main()
{ Output:
int a=3, b=5, res;
res=exp(a,b); 3 to the power of 5 = 243
printf(“\n %d to the power of %d=%d”, a, b, res);
return 0;
}

int exp(int a, int b) //function definition


{
register int res=1; //using register as storage class because res is
frequently accessed in all iterations of ‘for’ loop
int i;
for(i-0; i<=b; i++)
res=res*a;
return res;
}
Storage Classes Types Contd…
Example: ‘static’ Storage Class
#include<stdio.h>
void print(void);
int main()
{
printf("\n First call of print()"); Output:
print();
printf("\n\n Second call of print()"); First call of print()
print(); Static integer variable, x = 0
printf("\n\n Third call of print()"); Integer variable, y = 0
print();
return 0; Second call of print()
} Static integer variable, x = 1
Integer variable, y = 0
void print()
{ Third call of print()
static int x; Static integer variable, x = 2
int y = 0; Integer variable, y = 0
printf("\n Static integer variable, x = %d", x);
printf("\n Integer variable, y = %d", y);
x++;
y++;
}
Pre-Processor
The preprocessor is a program that processes the source code before it
passes through the compiler. It operates under the control of preprocessor
directive which is placed in the source program before the main().
Before the source code is passed through the compiler, it is examined by
the preprocessor for any preprocessor directives. In case, the program has
some preprocessor directives, appropriate actions are taken (and the source
program is handed over to the compiler.
The preprocessor directives are always preceded by a hash sign (#).
The preprocessor is executed before the actual compilation of program
code begins. Therefore, the preprocessor expands all the directives and take
the corresponding actions before any code is generated by the program
statements.
No semicolon (;) can be placed at the end of a preprocessor directive.
The advantages of using preprocessor directives in a C program include:
Program becomes readable and easy to understand
Program can be easily modified or updated
Program becomes portable as preprocessor directives makes it easy to
compile the program in different execution environments
Due to the aforesaid reason the program also becomes more efficient to
use.
Types of Preprocessor Directive

Preprocessor Directive

Unconditional Conditional

define line undef include error pragma

.
if else elif ifdef ifndef endif
1. #define
To define preprocessor macros we use #define. The #define statement is
also known as macro definition or simply a macro. There are two types of
macros-
1. Object like macro, and
2. Function like macro.
1. #define Contd…
1. Object like macro
An object-like macro is a simple identifier which will be replaced by a
code fragment. They are usually used to give symbolic names to numeric
constants. Object like macros do not take ant argument. It is the same what
we have been using to declare constants using #define directive. The
general syntax of defining a macro can be given as:,
Syntax: #define identifier string

The preprocessor replaces every occurrence of the identifier in the source


code by a string.
Example: #define PI 3.14
1. #define Contd…
2. Function-like macros
They are used to stimulate functions.
When a function is stimulated using a macro, the macro definition replaces the
function definition.
The name of the macro serves as the header and the macro body serves as the
function body. The name of the macro will then be used to replace the function
call.
The function-like macro includes a list of parameters.
References to such macros look like function calls. However, when a macro is
referenced, source code is inserted into the program at compile time. The
parameters are replaced by the corresponding arguments, and the text is inserted
into the program stream. Therefore, macros are considered to be much more
efficient than functions as they avoid the overhead involved in calling a function.
1. #define Contd…
The syntax of defining a function like macro can be given as
#define identifier(arg1,arg2,...argn) string
The following line defines the macro MUL as having two
parameters a and b and the replacement string (a * b):
#define MUL(a,b) (a*b)
Look how the preprocessor changes the following statement
provided it appears after the macro definition.
int a=2, b=3,c;
c = MUL(a,b);
1. #define Contd…
Example:
#include<stdio.h>
#define MUL(a,b) printf(“%d”,a*b)
void main()
{
int a=2,b=3,c;
clrscr();
c=MUL(a,b);
getch();
}
OPERATORS RELATED TO MACROS
# Operator to Convert to String Literals
The # preprocessor operator which can be used only in a function like macro
definition is used to convert the argument that follows it to a string literal. For
example:
#include<stdio.h>
#define PRINT(num) printf( #num " = %d", num)
main()
{
PRINT(20);
}
The macro call expands to
printf( “num" “ = %d", num)
Finally, the preprocessor will automatically concatenate two string literals into one
string. So the above statement will become
printf( “num = %d", num)
OPERATORS RELATED TO MACROS
2. Merge Operator (##)
At times you need macros to generate new tokens. Using the merge operator you
can concatenate two tokens into a third valid token. For example,
#include<stdio.h>
#define JOIN(A,B) A##B
main()
{
int i;
for(i=1;i<=3;i++)
printf("\n HI JOIN(USER, i) : ");
}
The above program would print
HI USER1
HI USER2
HI USER3
2. #include

An external file containing function, variables or macro definitions can be


included as a part of our program. This avoids the effort to re-write the
code that is already written.
The #include directive is used to inform the preprocessor to treat the
contents of a specified file as if those contents had appeared in the source
program at the point where the directive appears.
The #include directive can be used in two forms. Both forms makes the
preprocessor insert the entire contents of the specified file into the source
code of our program. However, the difference between the two is the way
in which they search for the specified.
#include <filename>
2. #include
This variant is used for system header files. When we include a file using
angular brackets, a search is made for the file named filename in a standard
list of system directories.
#include "filename"
This variant is used for header files of your own program. When we include
a file using double quotes, the preprocessor searches the file named
filename first in the directory containing the current file, then in the quote
directories and then the same directories used for <filename>.
3. #undef
As the name suggests, the #undef directive undefines or removes a macro
name previously created with #define. Undefining a macro means to cancel
its definition. This is done by writing #undef followed by the macro name
that has to be undefined.
4. #line
Compile the following C program.
#include<stdio.h>
main()
{
int a=10 :
printf("%d", a);
}
The above program has a compile time error because instead of a semi-colon
there is a colon that ends line int a = 10. So when you compile this program an
error is generated during the compiling process and the compiler will show an
error message with references to the name of the file where the error happened
and a line number. This makes it easy to detect the erroneous code and rectify it
4. #line Contd…
The #line directive enables the users to control the line numbers within the
code files as well as the file name that we want that appears when an error
takes place.
The syntax of #line directive is:
#line line_number filename
Here, line_number is the new line number that will be assigned to the next
code line. The line numbers of successive lines will be increased one by one
from this point on.
Filename is an optional parameter that redefines the file name that will appear
in case an error occurs. The filename must be enclosed within double quotes. If
no filename is specified then the compiler will show the original file name.
4. #line Contd…
For Example,
#include<stdio.h>
main()
{
#line 10 "Error.C"
int a=10:
#line 20
printf("%d, a);
}
4. Pragma Directives
The #pragma directive is used to control the actions of the compiler in a
particular portion of a program without affecting the program as a whole.
The effect of pragma will be applied from the point where it is included to
the end of the compilation unit or until another pragma changes its status.
A #pragma directive is an instruction to the compiler and is usually ignored
during preprocessing.
The syntax of using a pragma directive can be given as:
#pragma string
4. Pragma Directives Contd…

INSTRUCTION DESCRIPTION

COPYRIGHT To specify a copyright string

COPYRIGHT_DATE To specify a copyright date for the copyright string

HP_SHLIB_VERSION To create versions of a shared library routine

LOCALITY To name a code subspace

OPTIMIZE To turn the optimization feature on or off

OPT_LEVEL To set the level of optimization

VERSIONID To specify a version string


CONDITIONAL DIRECTIVES
A conditional directive is used instruct the preprocessor to select whether or not to
include a chunk of code in the final token stream passed to the compiler. The
preprocessor conditional directives can test arithmetic expressions, or whether a name is
defined as a macro, etc.
Conditional preprocessor directives can be used in the following situations:
A program may need to use different code depending on the machine or operating
system it is to run on.
The conditional preprocessor directive is very useful when you want to compile the same
source file into two different programs. While one program might make frequent
time-consuming consistency checks on its intermediate data, or print the values of those
data for debugging, the other program, on the other hand can avoid such checks.
The conditional preprocessor directives can be used to exclude code from the program
whose condition is always false but is needed to keep it as a sort of comment for future
reference.
1. #ifdef
#ifdef is the simplest sort of conditional preprocessor directive and is used
to check for the existence of macro definitions.
Its syntax can be given as:
#ifdef MACRO
controlled text
#endif
Example:
#ifdef MAX
int STACK[MAX];
#endif
2. #ifndef
The #ifndef directive is the opposite of #ifdef directive. It checks whether
the MACRO has not been defined or if its definition has been removed
with #undef.
#ifndef is successful and returns a non-zero value if the MACRO has not
been defined. Otherwise in case of failure, that is when the MACRO has
already been defined, #ifndef returns false (0).
The general format to use #ifndef is the same as for #ifdef:
#ifndef MACRO
controlled text
#endif
3. The #if Directive
The #if directive is used to control the compilation of portions of a source file.
If the specified condition (after the #if) has a nonzero value, the controlled text
immediately following the #if directive is retained in the translation unit.
The #if directive in its simplest form consists of
#if condition
//controlled text
#endif
While using #if directive, make sure that each #if directive must be matched by a
closing #endif directive. Any number of #elif directives can appear between the
#if and #endif directives, but at most one #else directive is allowed. However, the
#else directive (if present) must be the last directive before #endif.
4. The #else Directive
The #else directive can be used within the controlled text of an #if
directive to provide alternative text to be used if the condition is false.
The general format of #else directive can be given as:
#if condition
Controlled text1
#else
Controlled text2
#endif
5. The #elif Directive
The #elif directive is used when there are more than two possible
alternatives. The #elif directive like the #else directive is embedded within
the #if directive.
It has the following syntax:
#if condition
Controlled text1
#elif new_condition
Controlled text2
#else
Controlled text3
#endif
6. The #endif Directive
The general syntax of #endif preprocessor directive which is used to end
the conditional compilation directive can be given as:
#endif
The #endif directive ends the scope of the #if , #ifdef , #ifndef , #else , or
#elif directives.
The defined operator
We have seen that we can check the existence of a macro by using #ifdef directive.
However, the alternative to #ifdef directive is to use the defined unary operator.
The defined operator has one of the following forms:
defined MACRO or defined (MACRO)
The above expression evaluates to 1 if MACRO is defined and to 0 if it is not. The
defined operator helps you to check for macro definitions in one concise line
without having to use many #ifdef or #ifndef directives.
For example,
#if defined (MACRO1) && defined (MACRO2)
Controlled text1
#else
printf(“\n MACROS not defined”);
#endif
#error Directive
The #error directive is used to produce compiler-time error messages.
The syntax of this directive is:
#error string
The error messages include the argument string. The #error directive is usually used
to detect programmer inconsistencies and violation of constraints during
preprocessing.
When #error directive is encountered, the compilation process terminates and the
message specified in string is printed to stderr.
For example,
#ifndef SQUARE
#error MACRO not defined.
#endif

You might also like