C Programming AllClasses-Outline
C Programming AllClasses-Outline
1.1 Introduction
C is a general-purpose, structured programming language. Its instructions
consist of terms that resemble algebraic expressions, augmented by certain
English keywords such as if, else, for, do and while. C was the offspring of
the ‘Basic Combined Programming Language’ (BPCL) called B, developed in
the 1960’s at Cambridge University. B language was modified by Dennis
Ritchie and was implemented at Bell laboratories in 1972. The new language
was named C. Since it was developed along with the UNIX operating system,
it is strongly associated with UNIX. This operating system, which was also
developed at Bell laboratories, was coded almost entirely in C.
In C, the type of a variable determines what kinds of values it may take on.
The type of an object determines the set of values it can have and what
operations can be performed on it. This is a fairly formal, mathematical
definition of what a type is, but it is traditional (and meaningful). There are
several implications to remember:
1. The “set of values'' is finite. C's int type cannot represent all of the integers;
every version of C is accompanied by its own set of library functions, which are
written for the particular characteristics of the host computer.
A C program can be viewed as a group of building blocks called functions. A
function is a subroutine that may include one or more statements designed to
perform a specific task. To write a C program we first create functions and then
put them together. A C program may contain one or more sections shown in
Fig. 1.1.
The documentation section consists of a set of comment (remarks) lines giving
the name of the program, the author and other details which the programmer
would like to use later. Comments may appear anywhere within a program, as
long as they are placed within the delimiters /* and */ (e.g., /*this is a
comment*/). Such comments are helpful in identifying the program’s principal
features or in explaining the underlying logic of various program features.
Documentation section
Link section
Definition section
Global declaration section
main() function section
{
Declaration part
Executable part
}
Subprogram section
Function 1
Function 2
Function n
variables are called global variables and are declared in the global declaration
section that is outside of all the functions.
Every C program must have one main function section. This section contains
two parts, declaration part and executable part. The declaration part declares
all the variables used in the executable part. There is at least one statement in
the executable part. These two parts must appear between opening and
closing braces ({ and }). The program execution begins at the opening brace
and ends at the closing brace. The closing brace of the main function section
is the logical end of the program. All statements in the declaration and
executable parts end with a semicolon(;).
The subprogram section contains all the user-defined functions that are called
in the main function. User-defined functions are generally placed immediately
after the main function, although they may appear in any order.
All sections, except the main function section may be absent when they are
not required.
Self Assessment Questions
1. Using C language programmers can write their own library functions.
(True/False)
2. C is a _______ level programming language.
3. The documentation section contains a set of _________ lines.
4. Every C program must have one main() function. (True/False)
The second line says that we are defining a function named main. Most of the
time, we can name our functions anything we want, but the function name main
is special: it is the function that will be “called'' first when our program starts
running. The empty pair of parentheses indicates that our main function
accepts no arguments, that is, there isn't any information which needs to be
passed in when the function is called.
The braces { and } surround a list of statements in C. Here, they surround the
list of statements making up the function main.
The line
printf("Hello, world!\n");
is the first statement in the program. It asks that the function printf be called;
printf is a library function which prints formatted output. The parentheses
surround printf's argument list: the information which is handed to it which it
should act on. The semicolon at the end of the line terminates the statement.
printf 's first (and, in this case, only) argument is the string which it should
print. The string, enclosed in double quotes (""), consists of the words “Hello,
world!'' followed by a special sequence: \n. In strings, any two-character
sequence beginning with the backslash \ represents a single special character.
The sequence \n represents the “ 'new line'' character, which prints a carriage
return or line feed or whatever it takes to end one line of output and move down
to the next. (This program only prints one line of output, but it's still important
to terminate it.)
The second line in the main function is
return 0;
In general, a function may return a value to its caller, and main is no exception.
When main returns (that is, reaches its end and stops
functioning), the program is at its end, and the return value from main tells the
operating system (or whatever invoked the program that main is the main
function of) whether it succeeded or not. By convention, a return value of 0
indicates success.
Program 1.1: Area of a circle
Here is an elementary C program that reads in the radius of a circle,
calculates the area and then writes the calculated result.
#include <stdio.h> /* Library file access */
/* program to calculate the area of a circle */ /* Title (Comment) */
main() /* Function heading */
{
float radius, area; /*Variable declarations */
printf(“Radius=?”); /* Output statement(prompt) */
scanf(“%f”, &radius); /* Input statement */
area=3.14159*radius*radius; /* Assignment statement */
printf(“Area=%f” ,area); /* Output statement */
1.4 Constants
Constants in C refer to fixed values that do not change during the execution
of a program. C supports several types of constants as illustrated in Fig. 1.1.
Constants
Fig. 1.1
1.4.1 Integer constants
An integer constant refers to a sequence of digits. There are three types of
integers, namely decimal, octal and hexadecimal.
An octal integer constant consists of any combination of digits from the set 0
through 7, with a leading 0.
Examples: 045, 0, 0567
A sequence of digits preceded by 0x or 0X is considered as hexadecimal
integer. They may also include alphabets A through F or a through f. The letters
A through F represent numbers 10 through 15.
Examples: 0X6, 0x5B, 0Xbcd, 0X
The largest integer value that can be stored is machine-dependent. It is 32767
on 16-bit machines and 2,147,483,647 on 32-bit machines. It is also possible
to store larger integer constants on these machines by appending qualifiers
such as U, L and UL to the constants.
Examples: 54637U or 54637u (unsigned integer)
65757564345UL or 65757564345ul (unsigned long integer)
7685784L or 7685784l (long integer)
Program 1.1: Program to represent integer constants on a 16-bit
computer
/* Integer numbers on 16-bit machine */ main()
{
printf(“Integer values\n\n”);
printf(“%d %d %d\n”, 32767,32767+1,32767+10);
printf(“\n”);
printf(“Long integer values\n\n”);
printf(“%ld %ld %ld\n”, 32767L, 32767L+1L, 32767L+10L);
}
Type and execute the above program and observe the output
1.4.2 Real constants
The numbers containing fractional parts like 67.45 are called real (or floating
point) constants.
Examples: 0.0045, -8.5, +345.678
A real number may also be expressed in exponential (scientific) notation. The
general form is:
mantissa e exponent
The mantissa is either a real number expressed in decimal notation or an
integer. The exponent is an integer number with an optional plus or minus sign.
Manipal University Jaipur Page No.: 8
Programming in C Unit 1
The letter e separating the mantissa and the exponent can be written in either
lowercase or uppercase.
Examples: 04e4, 12e-2, -1.3E-2
7500000000 may be written as 7.5E9 or 75E8.
Floating point constants are normally represented as double-precision
quantities. However, the suffixes f or F may be used to force single precision
and l or L to extend double-precision further.
1.4.3 Character constants
A single character constant (or simple character constant) contains a single
character enclosed within a pair of single quote marks.
Examples: ‘6’, ‘X’, ‘;’
Character constants have integer values known as ASCII values. For example,
the statement
printf(“%d”, ‘a’);
would print the number 97, the ASCII value of the letter a. Similarly, the
statement
printf(“%c”, 97);
would print the letter a.
1.4.4 String constants
A string constant is a sequence of characters enclosed within double quotes.
The characters may be letters, numbers, special characters and blank space.
Examples: “Hello!”, “1947”, “5+3”
1.4.5 Backslash character constants
C supports some special backslash character constants that are used in output
functions. A list of such backslash character constants is given in Table 1.1.
Note that each one of them represents one character, although they consist of
two characters. These character combinations are called escape sequences.
Table 1.1: Backslash character constants
Constant Meaning
‘\b’ back space
‘\f’ form feed
‘\n’ new line
Informally, a variable (also called an object) is a place where you can store a
value so that you can refer to it unambiguously. A variable needs a name. You
can think of the variables in your program as a set of boxes, each with a label
giving its name; you might imagine that storing a value “in'' a variable consists
of writing the value on a slip of paper and placing it in the box.
Now let us see how integers and variables are declared. A declaration tells the
compiler the name and type of a variable you'll be using in your program. In its
simplest form, a declaration consists of the type, the name of the variable, and
a terminating semicolon: int i;
The above statement declares an integer variable i. long int i1, i2;
We can also declare several variables of the same type in one declaration,
separating them with commas as shown above.
The placement of declarations is significant. You can't place them just
anywhere (i.e. they cannot be interspersed with the other statements in your
program). They must either be placed at the beginning of a function, or at the
beginning of a brace-enclosed block of statements, or outside of any function.
Furthermore, the placement of a declaration, as well as its storage class,
controls several things about its visibility and lifetime, as we'll see later.
You may wonder why variables must be declared before use. There are two
reasons:
1. It makes things somewhat easier on the compiler; it knows right away what
kind of storage to allocate and what code to emit to store and manipulate
each variable; it doesn't have to try to intuit the programmer's intentions.
2. It forces a bit of useful discipline on the programmer: you cannot introduce
variables wherever you wish; you must think about them enough to pick
appropriate types for them. (The compiler's error messages to you, telling
you that you apparently forgot to declare a variable, are as often helpful as
they are a nuisance: they're helpful when they tell you that you misspelled
a variable, or forgot to think about exactly how you were going to use it.)
Most of the time, it is recommended to write one declaration per line. For the
most part, the compiler doesn't care what order declarations are in. You can
order the declarations alphabetically, or in the order that they're used, or to put
related declarations next to each other. Collecting all variables of the same
type together on one line essentially orders declarations by type, which isn't a
very useful order (it's only slightly more useful than random order).
A declaration for a variable can also contain an initial value. This initializer
consists of an equal sign and an expression, which is usually a single constant:
int i = 1;
int i1 = 10, i2 = 20;
Self Assessment Questions
10. The size of the Integers in C language is same in all the machines.
(True/False)
11. A _______ is a place where we can store values.
12. Size of int is _________ bits.
13. Which of the following tells the compiler the name and type of a variable
you'll be using in your program? ( declaration)
(a) declaration
(b) variables
(c) integers
(d) assignments
14. The _________ consists of an equal sign and an expression, which is
usually a single constant. (initializer)
15. A single declaration statement can contain variables of different types.
(True/False)
It's usually a matter of style whether you initialize a variable with an initializer
in its declaration or with an assignment expression near where you first use it.
That is, there's no particular difference between
int a = 10;
and
int a;
/* later... */ a = 10;
Self Assessment Questions
16. In C, variable names are case sensitive. (True/False)
17. A variable name in C consists of letters, numbers and ________ .
1.7 Summary
C is a general-purpose, structured programming language. Its instructions
consist of terms that resemble algebraic expressions, augmented by certain
English keywords such as if, else, for, do and while. C is characterized by the
ability to write very concise source programs, due in part to the large number
of operators included within the language. Every C program consists of one or
more functions, one of which must be called main. The program will always
begin by executing the main function. Additional function definitions may
precede or follow main.
Integers are whole numbers with a range of values supported by a particular
machine. Generally, integers occupy one word of storage, and since the word
sizes of machines vary (typically, 16 or 32 bits) the size of an integer that can
be stored depends on the computer. A variable (also called an object) is a
place where you can store a value. A declaration tells the compiler the name
and type of a variable you'll be using in your program. The assignment operator
= assigns a value to a variable.
6. A signed integer uses one bit for sign and remaining bits for the magnitude
of the number, whereas an unsigned integer uses all the bits to represent
magnitude.
7. A declaration consists of the type, the name of the variable, and a
terminating semicolon.
8. Variables (the formal term is “identifiers'') consist of letters, numbers, and
underscores. The capitalization of names in C is significant. You may not
use keywords (the words such as int and for which are part of the syntax
of the language) as the names of variables or functions (or as identifiers of
any kind).
9. The assignment operator (=) assigns a value to a variable.
1.11 Exercises
1. Explain the basic structure of a C program with an example.
2. What are the different steps in executing a C program?
3. Write a C program to convert Celsius to Fahrenheit and vice versa.
4. Write a program in C to add, subtract, multiply any 3 numbers.
Unit 2 Operators and Expressions
Structure:
2.1 Introduction
Objectives
2.2 Arithmetic Operators
2.3 Unary Operators
2.4 Relational and Logical Operators
2.5 The Conditional Operator
2.6 Library Functions
2.7 Bitwise Operators
2.8 The Increment and Decrement Operators
2.9 The Size of Operator
2.10 Precedence of operators
2.11 Summary
2.12 Terminal Questions
2.13 Answers to Self Assessment Questions
2.14 Answers to Terminal Questions
2.15 Exercises
2.1 Introduction
In the previous unit, you learned about the various features and structure of C
programs. You also learned how the variables in C are declared. In this unit,
you will learn about the operators that are available in C and how the
expressions can be formed to get the solutions of any problems.
C supports a rich set of operators. An operator is a symbol that tells the
computer to perform certain mathematical or logical manipulations. Operators
are used in programs to manipulate data and variables. They usually form a
part of the mathematical or logical expressions.
C operator can be classified into a number of categories. They include:
1. Arithmetic operators
2. Unary operator
3. Relational operators
4. Logical operators
5. Conditional operator
6. Bitwise operators
7. Increment and Decrement operators
Objectives:
After studying this subject, you should be able to:
8. explain different categories of operators
9. use operators on many operands
10. distinguish precedence and associativity of operators
11. explain library functions and their use
12. write small programs using different types of operators
days=days%30;
printf(“Months=%d Days=%d”, months,days);
}
+, -, *, and /. The relational operators take two values, look at them, and “return''
a value of 1 or 0 depending on whether the tested relation was true or false.
The complete set of relational operators in C is:
> less than
> = less than or equal
> greater than
> = greater thanor equal
== equal
!= not equal
For example, 1 < 2 is true(1), 3 > 4 is false(0), 5 == 5 is true(1), and 6 != 6 is
false(0).
The equality-testing operator is ==, not a single =, which is assignment. If you
accidentally write
if(a = 0)
(and you probably will at some point; everybody makes this mistake), it will not
test whether a is zero, as you probably intended. Instead, it will assign 0 to a,
and then perform the “true'' branch of the if statement if a is nonzero. But a will
have just been assigned the value 0, so the “true'' branch will never be taken!
(This could drive you crazy while debugging -- you wanted to do something if
a was 0, and after the test, a is 0, whether it was supposed to be or not, but
the “true'' branch is nevertheless not taken.)
The relational operators work with arbitrary numbers and generate true/false
values. You can also combine true/false values by using the Boolean
operators(also called the logical operators), which take true/false values as
operands and compute new true/false values. The three Boolean operators
are:
&& AND
|| OR
! NOT (takes one operand,“unary'')
The && (“and'') operator takes two true/false values and produces a true (1)
result if both operands are true (that is, if the left-hand side is true and the right-
hand side is true). The || (“or'') operator takes two true/false values and
produces a true (1) result if either operand is true. The ! (“not'') operator takes
a single true/false value and negates it, turning false to true and true to false
(0 to 1 and nonzero to 0). The logical operators && and || are used when we
Here we're expressing the relation “i is between 1 and 10'' as “1 is less than i
and i is less than 10.''
It's important to understand why the more obvious expression
if(1 < i < 10) /* WRONG */
would not work. The expression 1 < i < 10 is parsed by the compiler
analogously to 1 + i + 10. The expression 1 + i + 10 is parsed as (1 + i) + 10
and means “add 1 to i, and then add the result to 10.'' Similarly, the expression
1 < i < 10 is parsed as (1 < i) < 10 and means “see if 1 is less than i, and then
see if the result is less than 10.'' But in this case, “the result'' is 1 or 0,
depending on whether i is greater than 1. Since both 0 and 1 are less than 10,
the expression 1 < i < 10 would always be true in C, regardless of the value of
i!
Relational and Boolean expressions are usually used in contexts such as an if
statement, where something is to be done or not done depending on some
condition. In these cases what's actually checked is whether the expression
representing the condition has a zero or nonzero value. As long as the
expression is a relational or Boolean expression, the interpretation is just what
we want. For example, when we wrote
if(x > max)
the > operator produced a 1 if x was greater than max, and a 0 otherwise. The
if statement interprets 0 as false and 1 (or any nonzero value) as true.
But what if the expression is not a relational or Boolean expression? As far as
C is concerned, the controlling expression (of conditional statements like if)
can in fact be any expression: it doesn't have to “look like'' a Boolean
expression; it doesn't have to contain relational or logical operators. All C looks
at (when it's evaluating an if statement, or anywhere else where it needs a
true/false value) is whether the expression evaluates to 0 or nonzero. For
example, if you have a variable x, and you want to do something if x is nonzero,
where x or f() do not have obvious “Boolean'' names, you can read them as “if
x is nonzero'' or “if f() returns nonzero.''
Self Assessment Questions
3. The logical operators ___________ and _________ are used when
we want to test more than one condition and make decisions.
4. State whether the following statement is correct or not.
(Correct/Incorrect)
if(a<4<c)
b=c;
4.3 The Conditional Operator
The Conditional operator (ternary operator) pair “?:” is available in C to
construct conditional expressions of the form expr1?expr2:expr3
where expr1, expr2 and expr3 are expressions.
The operator ? : works as follows: expr1 is evaluated first. If it is
nonzero(true), then the expression expr2 is evaluated and becomes the value
of the expression. If expr1 is false, expr3 is evaluated and its value becomes
the value of the expression. For example, consider the following statements:
a=100; b=200;
c=(a>b)?a:b;
In this example, c will be assigned the value of b. This can be achieved using
the if..else statements as follows: if(a>b) c=a;
else c=b;
A typical set of library functions will include a large number of functions that
are common to most C compilers, such as those shown in table 2.1.
Function Purpose
abs(i) Return the absolute value of ( i is integer)
ceil(d) Round up to the next integer value(the smallest integer that is
greater than or equal to d)
cos(d) Return the cosine of d
exp(d) Raise e to the power d(e=Naperian constant)
fabs(d) Return the absolute value of d(d is double)
floor(d) Round down to the next integer value(the largest integer that does
not exceed d)
getchar() Enter a character from the standard input device
log(d) Return the natural logarithm of d
pow(d1,d2) Return d1 raised to the power d2
putchar(c) Send a character to the standard output device
rand() Return a random positive integer
sin(d) Return sine of d
sqrt(d) Return the square root of d
tan(d) Return the tangent of d
toascii(c) Convert value of argument to ASCII
tolower(c) Convert letter to lowercase
toupper(c) Convert letter to uppercase
Table 2.1
Program 2.2: Program to convert lowercase to uppercase
#include <stdio.h> /* Input/Output functions are available in stdio.h */
#include<ctype.h> /* Character functions are available in the file ctype.h */
main()
/* read a lowercase character and print its uppercase equivalent */ {
int lower, upper;
lower=getchar();
upper=toupper(lower);
putchar(upper);
}
Example:
x+=y+1;
This is same as the statement
x=x+(y+1);
The ++ and -- operators apply to one operand (they're unary operators). The
expression ++i adds 1 to i, and stores the incremented result back in i. This
means that these operators don't just compute new values; they also modify
the value of some variable. (They share this property -- modifying some
variable -- with the assignment operators; we can say that these operators all
have side effects. That is, they have some effect, on the side, other than just
computing a new value.)
The incremented (or decremented) result is also made available to the rest of
the expression, so an expression like k = 2 * ++i
means “add one to i, store the result back in i, multiply it by 2, and store that
result in k.'' (This is a pretty meaningless expression; our actual uses of ++
later will make more sense.)
Both the ++ and -- operators have an unusual property: they can be used in
two ways, depending on whether they are written to the left or the right of the
variable they're operating on. In either case, they increment or decrement the
variable they're operating on; the difference concerns whether it's the old or
the new value that's “returned'' to the surrounding expression. The prefix form
++i increments i and returns the incremented value. The postfix form i++
increments i, but returns the prior, nonincremented value. Rewriting our
previous example slightly, the expression k = 2 * i++
means “take i's old value and multiply it by 2, increment i, store the result of
the multiplication in k.''
The distinction between the prefix and postfix forms of ++ and -- will probably
seem strained at first, but it will make more sense once we begin using these
operators in more realistic situations.
For example,
a[i] = c;
i = i + 1;
using the ++ operator, we could simply write this as
a[i++] = c;
will behave exactly the same way and produce exactly the same results. (In
real code, postfix increment is probably more common, though prefix definitely
has its uses, too.)
printf(“%d\n”, (c<d)?1:0);
}
Execute the above program and observe the result.
Where the same operator appears twice (for example *) the first one is the
unary version.
Program 2.6: A program to illustrate evaluation of expressions
#include<stdio.h>
main()
/* Evaluation of expressions */
{
float a, b, c, x, y, z;
a=20;
b=2;
c=-23;
x = a + b / ( 3 + c * 4 - 1);
y = a - b / (3 + c) * ( 4 - 1);
Manipal University Jaipur Page No.: 31
Programming in C Unit 1
z= a - ( b / ( 3 + c ) * 2 ) - 1;
printf( “x=%f\n”, x);
printf(“y=%f\n”, y);
printf(“z=%f\n”, z);
}
Execute the above program and observe the result.
Program 2.7: Program to convert seconds to minutes and seconds
#include <stdio.h>
#define SEC_PER_MIN 60 // seconds in a minute
int main(void)
{
int sec, min, left;
2.11 Summary
C supports a rich set of operators. An operator is a symbol that tells the
computer to perform certain mathematical or logical manipulations. Operators
are used in programs to manipulate data and variables. A binary operator acts
on two operands. A unary operator acts upon a single operand to produce a
new value. Multiplication, division, and modulus all have higher precedence
than addition and subtraction. Relational and Boolean expressions are usually
used in contexts such as an if statement, where something is to be done or not
done depending on some condition. The C language is accompanied by a
number of library functions or built in functions that carry out various commonly
used operations or calculations. The size of operator is normally used to
determine the lengths of arrays and structures when their sizes are not known
to the programmer. It is also used to allocate memory space dynamically to
variables during execution of a program. Associativity is the order in which
consecutive operations within the same precedence group are carried out.
2.12 Terminal questions
1. If i=10 and j=12, what are the values of c and d after executing the
following program segment:
i++;
c=j++ + i;
d=++i + j++;
2. Suppose that x, y and z are integer variables which have been assigned
the values 2, 3 and 4, respectively. Evaluate the following expression and
determine the value of x.
x *= -2 * (y + z) / 3
3. Suppose that i is an integer variable whose value is 7 and c is a character
variable that represents the character ‘w’, evaluate the following logical
expression:
(i>=6) && (c==’w’)
4. Suppose that i is an integer variable whose value is 7 and f is a floating -
point variable whose value is 8.5. Evaluate the following expression: (i +
f) %4
5. What is meant by associativity?
3. true
4. Given expression is invalid because a floating point variable can not be
used in a modulus operation.
5. Associativity is the order in which consecutive operations within the same
precedence group are carried out.
2.15 Exercises
1. Suppose a=3, b=5, c=8 and d=4, give the output of the following:
a) x=a*b-c/d+4 b) z=a-b/d*c+10
2. Suppose i=5, j=8, k=10, then , what is the output of the following:
a) x=a++ -j b) y=k++ *j—
3. What is the precedence of operators? How expressions are evaluated
using the precedences?
4. Suppose a=7, b=11, find the output of the following:
a) x=(a>b)?b:a b) x=(a<b)?a:b
5. Explain the use of bitwise operators with suitable examples.
3.1 Introduction
In the previous unit, you learned about the operators that a C programming
language supports. You also learned how the operators are used in the
expressions of C programs. In this unit, you will learn about the data types that
are supported in C. You will also study about the Input/output operators which
makes C as the most efficient and powerful programming language. Integer is
one of the fundamental data types. All C compilers support four fundamental
data types, namely integer (int), character (char), floating point (float), and
double-precision floating point (double). Like integer data type, other data
types also offer extended data types such as long double and signed char.
C supports a rich set of operators. We have already used several of them, such
as =, +, -, *, / and %. An operator is a symbol that tells the computer to perform
certain mathematical or logical manipulations. Operators are used in programs
to manipulate data and variables. They usually form a part of the mathematical
or logical expressions.
count = 0; VARIABLES*/
while (count<N) { scanf(“%f”, &number); sum = sum + number; count
= count + 1;
}
average = sum / N;
}
Output
input the values of a,b and c
2 4 -16
Root1 = 2.00
Root2 = -4.00
input the values of a,b and c
123
roots are imaginary
3.2.1 Converting Integers to Floating-point and vice-versa
C permits mixing of constants and variables of different types in an expression,
but during evaluation it adheres to very strict rules of type conversion. We know
that the computer considers one operator at a time, involving two operands.
If the operands are of different types, the ‘lower’ type is automatically converted
to the ‘higher’ type before the operation proceeds. The result is of higher type.
Given below is the sequence of rules that are applied while evaluating
expressions.
All short type are automatically converted to int ; then
1. If one of the operands is long double, the other will be converted to
long double and the result will be long double;
2. else, if one of the operands is double, the other will be converted to
double and the result will be double;
3. else, if one of the operands is float, the other will be converted to float and
the result will be float;
4. else, if one of the operands is unsigned long int, the other will be
converted to unsigned long int and the result will be unsigned long int;
5. else if one of the operands is long int and the other is unsigned int, then:
• if unsigned int can be converted to long int, the unsigned int operand
will be converted as such and the result will be long int;
• else, both operands will be converted to unsigned long int and the
result will be unsigned long int;
6. else, if one of the operands is long int , the other will be converted to long
int and the result will be long int;
7. else, if one of the operands is unsigned int , the other will be converted to
unsigned int and the result will be unsigned int;
The final result of an expression is converted to type of the variable on the left
of the assignment sign before assigning the value to it. However, the following
changes are introduced during the final assignment:
1. float to int causes truncation of the fractional part.
2. double to float causes rounding of digits.
3. long int to int causes dropping of the excess higher order bits
y = p + m;
y = p + (double)m;
However, the second statement is preferable. It will work the same way on all
machines and is more readable.
Self Assessment Questions
6. Casting can be used to round-off a given value. (True/False)
7. The value of A in the expression A=(int)11.35/(int)14.5 is __________
8. If the value of X is 35.2, then the value of A in the expression:
A = (int)(X+0.5); is _____________ .
Note that with the format characters %d, the ASCII number of the character is
displayed. With the format character %c, the character corresponding to the
given ASCII number is displayed.
Self Assessment Questions
9. What is the format character to display the value of a char variable?
10. The output of the following C statement: printf(“%c”, 70); is .
3.5 Keywords
Keywords are the reserved words of a programming language. All the
keywords have fixed meanings and these meanings cannot be changed.
Keywords serve as basic building blocks for program statements. The list of all
keywords in ANSI C are listed in the Table 3.3.
Table 3.3: ANSI C Keywords
auto double int struct
break else long switch
case enum register typedef
char extern return union
const float short unsigned
continue for signed void
default goto sizeof volatile
do if static while
c=getchar();
The first statement declares that c is a character-type variable. The second
statement causes a single character to be entered from the keyboard and then
assign to c.
A companion function is putchar(), which writes one character to the “standard
output.'' (The standard output is usually the user's screen).
The syntax of the putchar() function is written as
putchar(character variable)
where character variable refers to some previously declared character
variable.
Example: char c;
putchar(c);
The first statement declares that c is a character-type variable. The second
statement causes the current value of c to be transmitted to the user monitor
where it will be displayed.
Using these two functions, we can write a very basic program to copy the input,
a character at a time, to the output:
Program 3.6: Program to copy the input, a character at a time, to the
output
#include <stdio.h>
/* copy input to output */ main()
{
int c;
c = getchar();
while(c != EOF)
{
running forever not waiting for something, you'll have to take more drastic
measures. Under Unix, control-C (or, occasionally, the DELETE key) will
terminate the current program, almost no matter what. Under MS-DOS,
control-C or control-BREAK will sometimes terminate the current program.
Self Assessment Questions
13. getchar() function is an output function.(True/False)
14. In order to stop reading the input character, you can use a value called
%c a single character
%d a decimal integer
%i an integer
%e, %f, %g a floating-point number
%o an octal number
%s a string
%x a hexadecimal number
%p a pointer
%n an integer equal to the number of characters read so far
%u an unsigned integer
%% a percent sign
scanf() reads the input, matching the characters from format. When a control
character is read, it puts the value in the next variable. Whitespaces (tabs,
spaces, etc) are skipped. Non-whitespace characters are matched to the input,
then discarded. If a number comes between the % sign and the control
character, then only that many characters will be entered into the variable. If
scanf() encounters a set of characters, denoted by the %[] control character,
then any characters found within the brackets are read into the variable. The
return value of scanf() is the number of variables that were successfully
assigned values, or EOF if there is an error.
char c;
char str[10];
scanf(“%d %f %c %s”, &i, &f, &c, str);
printf(“%d %f %c %s”, i, f, c, str);
}
Execute this program and observe the result.
Note that for a scanf() function, the addresses of the variable are used as the
arguments for an int, float and a char type variable. But this is not true for a
string variable because a string name itself refers to the address of a string
variable.
A s-control character is used to enter strings to string variables. A string that
includes whitespace characters can not be entered. There are ways to work
with strings that include whitespace characters. One way is to use the
getchar() function within a loop. Another way is to use gets() function which
will be discussed later.
It is also possible to use the scanf() function to enter such strings. To do so,
the s-control character must be replaced by a sequence of characters enclosed
in square brackets, designated as [...]. Whitespace characters may be included
within the brackets, thus accommodating strings that contain such characters.
Example:
#include<stdio.h> main() {
char str[80];
}
This example illustrates the use of the scanf() function to enter a string
consisting of uppercase letters and blank spaces. Please note that if you want
to allow lowercase letters to be entered, all the lowercase letters (i.e. from a-z)
must be included in the list of control string.
Formatted Output
Output data can be written from the computer onto a standard output device
using the library function printf(). This function can be used to output any
combination of numerical values, single characters and strings. It is similar to
the input function scanf(), except that its purpose is to display data rather than
Manipal University Jaipur Page No.: 48
Programming in C Unit 1
It is also possible to specify the width and precision of numbers and strings as
they are inserted ; For example, a notation like %3d means to print an int in a
field at least 3 spaces wide; a notation like %5.2f means to print a float or
double in a field at least 5 spaces wide, with two places to the right of the
decimal.)
To illustrate with a few more examples: the call
printf("%c %d %f %e %s %d%%\n", '3', 4, 3.24, 66000000, "nine", 8);
would print
3 4 3.240000 6.600000e+07 nine 8%
The call
printf("%d %o %x\n", 100, 100, 100);
would print
100 144 64
Successive calls to printf() just build up the output a piece at a time, so the
calls
printf("Hello, ");
printf("world!\n");
would also print Hello, world! (on one line of output).
Earlier we learned that C represents characters internally as small integers
corresponding to the characters' values in the machine's character set
(typically ASCII). This means that there isn't really much difference between a
character and an integer in C; most of the difference is in whether we choose
to interpret an integer as an integer or a character. printf is one place where
we get to make that choice: %d prints an integer value as a string of digits
representing its decimal value, while %c prints the character corresponding to
a character set value. So the lines
char c = 'A';
int i = 97;
printf("c = %c, i = %d\n", c, i);
would print c as the character A and i as the number 97. But if, on the other
hand, we called
printf("c = %d, i = %c\n", c, i);
we'd see the decimal value (printed by %d) of the character 'A', followed by the
character (whatever it is) which happens to have the decimal value 97.
You have to be careful when calling printf(). It has no way of knowing how
many arguments you've passed it or what their types are other than by looking
for the format specifiers in the format string. If there are more format specifiers
(that is, more % signs) than the arguments, or if the arguments have the wrong
types for the format specifiers, printf() can misbehave badly, often printing
nonsense numbers or (even worse) numbers which mislead you into thinking
that some other part of your program is broken.
#include<stdio.h>
main()
{
char line[80];
gets(line);
puts(line);
}
This program uses gets() and puts() functions rather than scanf() and printf(),
to transfer the line of text into and out of the computer.
3.10 Summary
Floating point(or real) numbers are stored in 32 bit (on all 16 bit and 32 bit
machines), with 6 digits of precision. Floating point numbers are defined in C
by the keyword float. When the accuracy provided by a float number is not
sufficient, the type double can be used to define the number. Characters are
usually stored in 8 bits (one byte) of internal storage. Like integer data type
other data types also offer extended data types such as long double and
signed char. C permits mixing of constants and variables of different types in
an expression, but during evaluation it adheres to very strict rules of type
conversion. When one of the operands is real and the other is integer, the
expression is called a mixed-mode arithmetic expression. There are instances
when we want to force a type conversion in a way that is different from the
automatic conversion. That is, by using type cast operator. All keywords have
fixed meanings and these meanings cannot be changed.
getchar(), putchar(), scanf(), printf(), gets() and puts() are the commonly
used input/output functions in C. These functions are used to transfer of
information between the computer and the standard input/output devices.
getchar() and putchar() are the two functions to read and write single
character. scanf() and printf() are the two formatted input/output functions.
These functions can handle characters, numerical values and strings as well.
gets() and puts() functions are used to handle strings. scanf(), printf(), gets()
and puts() functions are used in interactive programming.
7. 0
8. 35
9. %c
10. False
11. Lowercase
12. False
13. False
14. EOF
15. Control
16. %x
17. True
18. 64, 12, 4B
19. %o
20. a
21. False
22. String
23. False
3.14 Exercises
1. Represent the following numbers using scientific notation:
a) 0.001 b) -1.5
2. Represent the following scientific numbers into decimal notation: a)
1.0E+2 b) 0.001E-2
3. What is unsigned char? Explain.
4. What is short char? Explain.
5. Distinguish between float and double data types.
6. Write a program to print the factors of a given number.
7. Given the length of a side, write a C program to compute surface area
and volume of a cube.
8. Write a program to reverse a number and find sum of the digits.
9. Write a program to print the multiplication table for any given number.
10. Write a program to check whether a given number is palindrome.
4.1 Introduction
In the previous unit, you studied about the data types that are supported in C
and the types of Input/output operators available in C. This unit will enable you
to use the various types of control statements for making a decision in C.
Statements are the “steps'' of a program. Most statements compute and assign
values or call functions, but we will eventually meet several other kinds of
statements as well. By default, statements are executed in sequence, one after
another. We can, however, modify that sequence by using control flow
constructs such that a statement or group of statements is executed only if
some condition is true or false. This involves a kind of decision making to see
whether a particular condition has occurred or not and then direct the computer
to execute certain statements accordingly.
C language possesses such decision making capabilities and supports the
following statements known as the control or decision making statements.
• if statement
• switch statement
• goto statement
• conditional operator statement
You will also study about the loops in this unit. Loops generally consist of two
parts: one or more control expressions which control the execution of the loop,
and the body, which is the statement or set of statements which is executed
over and over.
As far as C is concerned, a true/false condition can be represented as an
integer. (An integer can represent many values; here we care about only two
values: “true'' and “false.'' The study of mathematics involving only two values
is called Boolean algebra, after George Boole, a mathematician who refined
this study.) In C, “false'' is represented by a value of 0 (zero), and “true'' is
represented by any value that is nonzero. Since there are many nonzero
values (at least 65,534, for values of type int), when we have to pick a specific
value for “true,'' we'll pick 1.
Do...while loop is used in a situation where we need to execute the body of the
loop before the test is performed. The for loop is used to execute the body of
the loop for a specified number of times. The break statement is used to exit
any loop.
Objectives:
After studying this unit, you should be able to:
• control the flow of execution of statements using two-way decision and
multipath decision.
• branch unconditionally from one point to another in the program.
• evaluate the conditional expressions.
• repeat the execution of statements by checking the condition before the
loop body is executed and by checking the condition at the end of the loop.
• exit from the loop depending on some condition.
• break the current iteration and continue with next iteration of loop.
4.2 The goto statement
C supports the goto statement to branch unconditionally from one point to
another in the program. Although it may not be essential to use the goto
statement in a highly structured language like C, there may be occasions when
the use of goto might be desirable.
The goto requires a label in order to identify the place where the branch is to
be made. A label is any valid variable name, and must be followed by a colon.
The label is placed immediately before the statement where the control is to
be transferred. The general forms of goto and label statements are shown
below:
The label can be anywhere in the program either before the goto or after the
goto label; statement.
During execution of the program when a statement like
goto first;
is met, the flow of control will jump to the statement immediately following the
label first. This happens unconditionally.
Note that a goto breaks the normal sequential execution of the program. If the
label is before the statement goto label; a loop will be formed and some
statements will be executed repeatedly. Such a jump is known as a backward
jump. On the other hand, if the label is placed after the goto label; some
statements will be skipped and the jump is known as the forward jump.
A goto is often used at the end of a program to direct the control to go to the
input statement, to read further data. Consider the following example:
Program 4.1: Program showing unconditional branching
main()
{
double a, b;
read:
printf(“enter the value of a\n”);
Manipal University Jaipur Page No.: 59
Programming in C Unit 1
scanf(“%f”, &a);
if (a<0) goto read;
b=sqrt(a);
printf(“%f %f \n”,a, b);
goto read;
}
This program is written to evaluate the square root of a series of numbers read
from the terminal. The program uses two goto statements, one at the end,
after printing the results to transfer the control back to the input statements and
the other to skip any further computation when the number is negative.
Due to the unconditional goto statement at the end, the control is always
transferred back to the input statement. In fact, this program puts the computer
in a permanent loop known as an infinite loop.
Self Assessment Questions
1. The goto requires a ________ in order to identify the place where the
branch is to be made.
2. goto is an unconditional branching statement. (True/False)
if( expression )
{
statement 1;
statement 2;
statement n;
}
As a general rule, anywhere the syntax of C calls for a statement, you may
write a series of statements enclosed by braces. (You do not need to, and
should not, put a semicolon after the closing brace, because the series of
statements enclosed by braces is not itself a simple expression statement.)
When you have one if statement (or loop) nested inside another, it's a very
good idea to use explicit braces {}, as shown, to make it clear (both to you and
to the compiler) how they're nested and which else goes with which if. It's also
a good idea to indent the various levels, also as shown, to make the code more
readable to humans. Why do both? You use indentation to make the code
visually more readable to yourself and other humans, but the compiler doesn't
pay attention to the indentation (since all whitespace is essentially equivalent
and is essentially ignored). Therefore, you also have to make sure that the
punctuation is right.
The condition of a switch statement is a value. The case says that if it has the
value of whatever is after that case then do whatever follows the colon. The
break is used to break out of the case statements. break is a keyword that
breaks out of the code block, usually surrounded by braces, which it is in. In
this case, break prevents the program from falling through and executing the
code in all the other case statements. An important thing to note about the
switch statement is that the case values may only be constant integral
expressions. It isn't legal to use case like this: int a = 10; int b = 10; int c = 20;
switch ( a ) { case b:
/* Code */
break;
case c:
/* Code */
break;
default:
/* Code */ break;
}
The default case is optional, but it is wise to include it as it handles any
unexpected cases. It can be useful to put some kind of output to alert you to
the code entering the default case if you don't expect it to. Switch statements
serve as a simple way to write long if statements when the requirements are
met. Often it can be used to process input from a user.
Example: Below is a sample program, in which not all of the proper functions
are actually declared, but which shows how one would use switch in a program.
#include <stdio.h>
void playgame(); void loadgame(); void playmultiplayer(); int main()
{
int input;
printf( "1. Play game\n" );
printf( "2. Load game\n" );
printf( "3. Play multiplayer\n" );
printf( "4. Exit\n" );
printf( "Selection: " );
scanf( "%d", &input );
switch ( input ) {
executing the body of the loop, the condition is tested. Therefore it is called an
entry-controlled loop. The following example repeatedly doubles the number
2 (2, 4, 8, 16, ...) and prints the resulting numbers as long as they are less than
1000:
int x = 2;
You use a while loop when you have a statement or group of statements which
may have to be executed a number of times to complete their task. The
controlling expression represents the condition “the loop is not done'' or
“there's more work to do.'' As long as the expression is true, the body of the
loop is executed; presumably, it makes at least some progress at its task.
When the expression becomes false, the task is done, and the rest of the
program (beyond the loop) can proceed. When we think about a loop in this
way, we can see an additional important property: if the expression evaluates
Manipal University Jaipur Page No.: 68
Programming in C Unit 1
to “false'' before the very first trip through the loop, we make zero trips through
the loop. In other words, if the task is already done (if there's no work to do)
the body of the loop is not executed at all. (It's always a good idea to think
about the “boundary conditions'' in a piece of code, and to make sure that the
code will work correctly when there is no work to do, or when there is a trivial
task to do, such as sorting an array of one number. Experience has shown that
bugs at boundary conditions are quite common.)
Program 4.5: Program to find largest of n numbers main() { int num, large,
n, i;
clrscr();
printf("enter number of numbers \n");
scanf(“%d”,&n);
large=0;
i=0;
while(i<n)
{
printf("\n enter number ");
scanf(“%d”, &num);
if(large<num)
large=num;
i++;
} printf("\n large = %d”, large);
}
scanf(“%f”, &acc);
sum=x;
term=x;
while ((fabs(term))>acc)
{
term=-term*x*x/((2*i)*(2*i+1));
sum+=term;
i++;
} printf"\nsum of sine series is %f", sum); }
Self Assessment Questions
8. A ____________ loop starts out like an if statement .
9. while is an entry-controlled loop. (True/False)
2 4 6 8 ........... .................. 20
3 6 9 12 ............ .................. 30
4 .................. 40
10 100
// Program to print multiplication table main() {
int rowmax=10,colmax=10,row,col,x;
printf(" Multiplication table\n");
printf(" ................................... \n");
row=1;
do
{
col=1;
do
{
x=row*col;
printf(“%4d”, x);
col=col+1;
}
while (col<=colmax);
printf(“\n”);;
row=row+1;
}
while(row<=rowmax);
Printf(" .........................................................................................................\
n");
}
It's also worth noting that a for loop can be used in more general ways than the
simple, iterative examples we've seen so far. The “control variable'' of a for
loop does not have to be an integer, and it does not have to be incremented
by an additive increment. It could be “incremented'' by a multiplicative factor
(1, 2, 4, 8, ...) if that was what you needed, or it could be a floating-point
variable, or it could be another type of variable which we haven't met yet which
would step, not over numeric values, but over the elements of an array or other
data structure. Strictly speaking, a for loop doesn't have to have a “control
variable'' at all; the three expressions can be anything, although the loop will
make the most sense if they are related and together form the expected
initialize, test, increment sequence.
The powers-of-two example using for is:
int x;
for(x = 2; x < 1000; x = x * 2) printf("%d\n", x);
There is no earth-shaking or fundamental difference between the while and
for loops. In fact, given the general for loop
for(expr1; expr2; expr3)
statement
you could usually rewrite it as a while loop, moving the initialize and increment
expressions to statements before and within the loop:
expr1;
while(expr2)
{
statement
expr3;
}
Similarly, given the general while loop while(expr) statement
you could rewrite it as a for loop:
for(; expr; )
statement
Another contrast between the for and while loops is that although the test
expression (expr2) is optional in a for loop, it is required in a while loop. If you
leave out the controlling expression of a while loop, the compiler will complain
about a syntax error. (To write a deliberately infinite while loop, you have to
supply an expression which is always nonzero. The most obvious one would
simply be while(1) .)
If it's possible to rewrite a for loop as a while loop and vice versa, why do they
both exist? Which one should you choose? In general, when you choose a for
loop, its three expressions should all manipulate the same variable or data
structure, using the initialize, test, increment pattern. If they don't manipulate
the same variable or don't follow that pattern, wedging them into a for loop
buys nothing and a while loop would probably be clearer. (The reason that one
loop or the other can be clearer is simply that, when you see a for loop, you
expect to see an idiomatic initialize/ test/increment of a single variable, and if
the for loop you're looking at doesn't end up matching that pattern, you've been
momentarily misled.)
Program 4.8: A Program to find the factorial of a number
void main()
{
int M,N;
long int F=1;
clrscr();
printf(“enter the number\n”)";
scanf(“%d”,&N);
if(N<=0)
F=1;
else
{
for(M=1;M<=N;M++)
F*=M;
}
printf(“the factorial of the number is %ld”,F); getch();
}
for(i=1;i<10;i++)
{
for(j=1;j<5;j++)
{
4.9 The break statement and continue statement
The purpose of break statement is to break out of a loop (while, do while, or
for loop) or a switch statement. When a break statement is encountered
inside a loop, the loop is immediately exited and the program continues with
the statement immediately following the loop. When the loops are nested, the
break would only exit from the loop containing it. That is, the break would exit
only a single loop.
Syntax : break;
The above program displays the numbers from 1to 4 and prints the message
“Broke out of loop when 5 is encountered.
Syntax: continue;
Program 4.10: Program to illustrate the use of continue statement.
void main ( ) {
int x;
for (x=1; x<=10; x++)
{ if (x==5)
continue; /* skip remaining code in loop only if x == 5 */
printf (“%d\n”, x);
} printf(“\nUsed continue to skip”);
}
The above program displays the numbers from 1to 10, except the number 5.
4. Write the output that will be generated by the following C program: void
main()
{
if (i%5 == 0)
{
x+=i;
printf(“%d\t”, i);
}
i++;
}
printf(“\nx=%d”; x);
5. Write the output that will be generated by the following C program: void
main()
if (i%5 == 0)
{
x++;
printf(“%d\t”, x);
}
++i;
} while (i<20);
printf(“\nx=%d”, x);
printf(“enter a number\n”);
scanf(“%d”,&no);
if (no%2==0)
printf(“even number\n”);
else printf(“odd number\n”);
}
4. 0 5 10 15
x = 30
5. 1 2 3 4
x=4
4.14 Exercises
1. Explain different types of if statements with examples.
2. Explain the syntax of switch statement with an example.
3. Write a program to check whether a given number is odd or even using
switch statement.
4. Write a program to find the smallest of 3 numbers using if-else statement.
5. Write a program to find the roots of a quadratic equation.
6. Compare the following statements
a) while and do...while
b) break and continue
7. Write a program to compute the sum of digits of a given number using
while loop.
8. Write a program that will read a positive integer and determine and print
its binary equivalent using do...while loop.
9. The numbers in the sequence
1 1 2 3 5 8 13 ............
are called Fibonacci numbers. Write a program using do.while loop to
calculate and print the first n Fibonacci numbers.
10. Find errors, if any, in each of the following segments. Assume that all the
variables have been declared and assigned values.
(a)
while (count !=10);
{
count = 1;
sum = sum + x;
count = count + 1;
}
(b)
do;
total = total + value;
scanf(“%f”, &value);
while (value ! =999);
11. Write programs to print the following outputs using for loops.
(a) 1 b) 1
22 2 2
333 3 3 3
4444 4 4 4 4
12. Write a program to read the age of 100 persons and count the number of
persons in the age group 50 to 60. Use for and continue statements.
13. Write a program to print the multiplication table using nested for loops.
Unit 5 Functions
Structure:
5.1 Introduction
Objectives
5.2 Function Basics
5.3 Function Prototypes
5.4 Recursion
5.5 Function Philosophy
5.6 Summary
5.7 Terminal Questions
5.8 Answers for Self Assessment Questions
5.9 Answers for Terminal Questions
5.10 Exercises
5.1 Introduction
In the previous unit, you studied about the control statements and its usage in
C. You also studied how those control statements are helpful in making
decisions when various types of conditions and options are available in the
problem. In this unit, you will study about what a function is.
A function is a “black box'' that we've locked part of our program into. The idea
behind a function is that it compartmentalizes part of the program, and in
particular, that the code within the function has some useful properties:
It performs some well-defined task, which will be useful to other parts of the
program. It might be useful to other programs as well; that is, we might be able
to reuse it (and without having to rewrite it).
The rest of the program doesn't have to know the details of how the function is
implemented. This can make the rest of the program easier to think about.
The function performs its task well. It may be written to do a little more than is
required by the first program that calls it, with the anticipation that the calling
program (or some other program) may later need the extra functionality or
improved performance. (It's important that a finished function do its job well,
otherwise there might be a reluctance to call it, and it therefore might not
achieve the goal of reusability.)
By placing the code to perform the useful task into a function, and simply calling
the function in the other parts of the program where the task must be
performed, the rest of the program becomes clearer: rather than having some
large, complicated, difficult-to-understand piece of code repeated wherever the
task is being performed, we have a single simple function call, and the name
of the function reminds us which task is being performed.
Since the rest of the program doesn't have to know the details of how the
function is implemented, the rest of the program doesn't care if the function is
re-implemented later, in some different way (as long as it continues to perform
its same task, of course!). This means that one part of the program can be
rewritten, to improve performance or add a new feature (or simply to fix a bug),
without having to rewrite the rest of the program.
Functions are probably the most important weapon in our battle against
software complexity. You'll want to learn when it's appropriate to break
processing out into functions (and also when it's not), and how to set up
function interfaces to best achieve the qualities mentioned above: reusability,
information hiding, clarity, and maintainability.
Objectives:
After studying this unit, you should be able to:
• explain the importance of functions
• implement the concepts of formal arguments and actual arguments
• explain function declaration(function prototypes) and function definition
• use the concept of recursion
• explain how the concept of functions reduces software complexity
(statements) for carrying out the task the function is supposed to perform; and
it may give you back a return value, of a particular type.
In general terms, the first line can be written as
data-type name(data-type parameter 1, data-type parameter 2, ..., data-type
parameter n)
Example 5.1: Here is a very simple function, which accepts one argument,
multiplies it by 4, and hands that value back.
int multbyfour(int x)
{
int retval;
retval = x * 4;
return retval;
}
On the first line we see the return type of the function (int), the name of the
function (multbyfour), and a list of the function's arguments, enclosed in
parentheses. Each argument has both a name and a type; multbyfour accepts
one argument, of type int, named x. The name x is arbitrary, and is used only
within the definition of multbyfour. The caller of this function only needs to
know that a single argument of type int is expected; the caller does not need
to know what name the function will use internally to refer to that argument. (In
particular, the caller does not have to pass the value of a variable named x.)
Next we see, surrounded by the familiar braces, the body of the function itself.
This function consists of one declaration (of a local variable retval) and two
statements. The first statement is a conventional expression statement, which
computes and assigns a value to retval, and the second statement is a return
statement, which causes the function to return to its caller, and also specifies
the value which the function returns to its caller.
In general term, a return statement is written as
return expression
The return statement can return the value of any expression, so we don't really
need the local retval variable; this function can also be written as
int multbyfour(int x)
{
return x * 4;
}
How do we call a function? We've been doing so informally since day one, but
now we have a chance to call one that we've written, in full detail. The
arguments in the function call are referred to as actual arguments or actual
parameters, in contrast to the formal arguments that appear in the first line of
the function definition.
Here is a tiny skeletal program to call multbyfour:
#include <stdio.h>
extern int multbyfour(int);
int main()
{
int i, j;
i = 5;
j = multbyfour(i);
printf("%d\n", j);
return 0;
}
This looks much like our other test programs, with the exception of the new line
extern int multbyfour(int);
This is an external function prototype declaration. It is an external declaration,
in that it declares something which is defined somewhere else. (We've already
seen the defining instance of the function multbyfour, but may be the compiler
hasn't seen it yet.) The function prototype declaration contains the three pieces
of information about the function that a caller needs to know: the function's
name, return type, and argument type(s). Since we don't care what name the
multbyfour function will use to refer to its first argument, we don't need to
mention it. (On the other hand, if a function takes several arguments, giving
them names in the prototype may make it easier to remember which is which,
so names may optionally be used in function prototype declarations.) Finally,
to remind us that this is an external declaration and not a defining instance, the
prototype is preceded by the keyword extern.
The presence of the function prototype declaration lets the compiler know that
Manipal University Jaipur Page No.: 85
Programming in C Unit 1
}
int max(int a, int b)
{
int c;
c=(a>=b)?a:b;
return c;
}
Please execute this program and observe the result.
Function calls can span several levels within a program; function A can call
function B, which can call function C and so on.
Program 5.2: Program to check whether a given integer is a perfect
square or not.
#include<stdio.h>
main()
{
int psquare(int);
int num;
printf(“ Enter the number:”);
scanf(“%d”, &num);
if(psquare(num)) /* main() calls the function psquare() */
{ printf(“%d is a perfect square\n”);
else
printf(“%d is not a perfect square\n”);
}
}
int psquare(int x)
{
int positive(int);
float sqr;
if(positive(x)) /* psquare() in turn calls the function positive() */ { sqr=sqrt(x));
if(sqr-int(sqr))==0)
return 1;
else
return 0;
}
int positive(int m) {
if(m>0)
return 1;
else return 0;
}
Execute the above program and observe the result.
In the above program the main function calls the function psquare() and it in
turn calls the function positive() to check whether the number to be checked
for perfect square is a positive or not before checking.
The return statement can be absent altogether from a function definition,
though this is generally regarded as a poor programming practice. If a
function reaches end of the block without encountering a return statement,
control simply reverts back to the calling portion of the program without
returning any information. Using an empty return statement(without the
accompanying expressions) is recommended.
Example 5.2: The following function accepts two integers and determines
the larger one, which is then written out. The function doesn’t return any
information to the calling program.
void max(int x, int y)
{
int m;
m=(x>=y)?x:y;
printf(“ The larger integer is=%d\n”, m); return;
}
Self Assessment Questions
1. The function main() is optional in a C program. (True/False)
2. If the function is defined elsewhere (not in the same program where it is
called), the function prototype must be preceded by the keyword
other standard library functions we call, there will be other “header files'' to
include.) Finally, one more thing about external function prototype declarations:
we've said that the distinction between external declarations and defining
instances of normal variables hinges on the presence or absence of the
keyword extern. The situation is a little bit different for functions. The “defining
instance'' of a function is the function, including its body (that is, the brace-
enclosed list of declarations and statements implementing the function). An
external declaration of a function, even without the keyword extern, looks
nothing like a function declaration. Therefore, the keyword extern is optional
in function prototype declarations. If you wish, you can write
int multbyfour(int);
and this is just like an external function prototype declaration as
extern int multbyfour(int);
(In the first form, without the extern, as soon as the compiler sees the
semicolon, it knows it's not going to see a function body, so the declaration
can't be a definition.) You may want to stay in the habit of using extern in all
external declarations, including function declarations, since “extern = external
declaration'' is an easier rule to remember.
Program 5.3: Program to illustrate that the function prototype is optional
in the caller function. The program is to convert a character from
lowercase to uppercase.
#include<stdio.h>
char lower_to_upper(char ch) /* Function definition precedes main()*/
{
char c;
c=(ch>=’a’ && ch<=’z’) ? (‘A’+ch-‘a’):ch;
return c;
}
main()
{
char lower, upper;
/* char lower_to_upper(char lower); */ /* Function prototype is
optional here*/
printf(“Please enter a lowercase character:”);
scanf(“%c”, &lower);
upper=lower_to_upper(lower);
printf(“\nThe uppercase equivalent of %c is %c\n”, lower, upper);
}
z=fun(x, y);
5.4 Recursion
Recursion is a process by which a function calls itself repeatedly, until some
specified condition has been met. The process is used for repetitive
computations in which each action is stated in terms of a previous result. Many
repetitive problems can be written in this form.
In order to solve a problem recursively, two conditions must be satisfied. First,
the problem must be written in a recursive form, and the second, the problem
statement must include a stopping condition.
Example 5.3: Factorial of a number. Suppose we wish to calculate the
factorial of a positive integer, n. We would normally express this problem as
n!=1 x 2 x 3 x ... x n.
This can also be written as n!=n x (n-1)!. This is the recursive statement of the
problem in which the desired action(the calculation of n!) is expressed in terms
of a previous result (the value of (n-1)! which is assumed to be known). Also,
we know that 0!=1 by definition. This expression provides stopping condition
for the recursion.
Thus the recursive definition for finding factorial of positive integer n can be
written as:
fact(n)={ 1 if n=0
n x fact(n-1) otherwise}
Program 5.4: Program to find factorial of a given positive integer
#include<stdio.h> main() {
int n;
long int fact(int);
/* Read in the integer quantity*/ scanf(“%d”, &n);
/*calaculate and display the factorial*/ printf(“n!=%ld\n”, fact(n));
}
long int fact(int n)
{
if(n==0)
return(1);
else
return (n*fact(n-1));
}
Please execute this program and observe the result.
Example 5.4: The Towers of Hanoi. The Towers of Hanoi is a game played
with three poles and a number of different sized disks. Each disk has a hole in
the center, allowing it to be stacked around any of the poles. Initially, the disks
are stacked on the leftmost pole in the order of decreasing size, i.e, the largest
on the bottom, and the smallest on the top as illustrated in Figure 5.1.
#include<stdio.h>
main()
Manipal University Jaipur Page No.: 95
Programming in C Unit 1
{
int n=5;
int fun(int n);
printf(“%d\n”, fun(n));
}
int fun(int n)
{
if(n==0)
return 0;
else
return (n+fun(n-1));
}
5.5 Function Philosophy
What makes a good function? The most important aspect of a good “building
block'' is that have a single, well-defined task to perform. When you find that a
program is hard to manage, it's often because it has not been designed and
broken up into functions cleanly. Two obvious reasons for moving code down
into a function are because:
1. It appeared in the main program several times, such that by making it a
function, it can be written just once, and the several places where it used
to appear can be replaced with calls to the new function.
2. The main program was getting too big, so it could be made (presumably)
smaller and more manageable by lopping part of it off and making it a
function.
These two reasons are important, and they represent significant benefits
of well-chosen functions, but they are not sufficient to automatically identify
a good function. As we've been suggesting, a good function has at least
these two additional attributes:
3. It does just one well-defined task, and does it well.
4. Its interface to the rest of the program is clean and narrow.
Attribute 3 is just a restatement of two things we said above. Attribute 4 says
that you shouldn't have to keep track of too many things when calling a
function. If you know what a function is supposed to do, and if its task is simple
and well-defined, there should be just a few pieces of information you have to
give it to act upon, and one or just a few pieces of information which it returns
to you when it's done. If you find yourself having to pass lots and lots of
information to a function, or remember details of its internal implementation to
make sure that it will work properly this time, it's often a sign that the function
is not sufficiently well-defined. (A poorly-defined function may be an arbitrary
chunk of code that was ripped out of a main program that was getting too big,
such that it essentially has to have access to all of that main function's local
variables.)
The whole point of breaking a program up into functions is so that you don't
have to think about the entire program at once; ideally, you can think about just
one function at a time. We say that a good function is a “black box,'' which is
supposed to suggest that the “container" it's in is opaque - callers can't see
inside it (and the function inside can't see out). When you call a function, you
only have to know what it does, not how it does it. When you're writing a
function, you only have to know what it's supposed to do, and you don't have
to know why or under what circumstances its caller will be calling it. (When
designing a function, we should perhaps think about the callers just enough to
ensure that the function we're designing will be easy to call, and that we aren't
accidentally setting things up so that callers will have to think about any internal
details.)
Some functions may be hard to write (if they have a hard job to do, or if it's
hard to make them do it truly well), but that difficulty should be
compartmentalized along with the function itself. Once you've written a “hard''
function, you should be able to sit back and relax and watch it do that hard
work on call from the rest of your program. It should be pleasant to notice (in
the ideal case) how much easier the rest of the program is to write, now that
the hard work can be deferred to this workhorse function.
(In fact, if a difficult-to-write function's interface is well-defined, you may be able
to get away with writing a quick-and-dirty version of the function first, so that
you can begin testing the rest of the program, and then go back later and
rewrite the function to do the hard parts. As long as the function's original
interface anticipated the hard parts, you won't have to rewrite the rest of the
program when you fix the function.)
The functions are important for far more important reasons than just saving
typing. Sometimes, we'll write a function which we only call once, just because
}
Self Assessment Questions
12. By modularizing the problem into different sub problems. Each sub
problem can be implemented as a _________ .
13. The main purpose of function is to save typing time. (True/False)
5.6 Summary
A function is a self-contained program segment that carries out some specific,
well-defined task. When you find that a program is hard to manage, it's often
because it has not been designed and broken up into functions cleanly. A
function is a “black box'' that we've locked part of our program into. The idea
behind a function is that it compartmentalizes part of the program. The function
main() is must in every C program. The function prototype is nothing but the
function declaration. Recursion is a process by which a function calls itself
repeatedly, until some specified condition has been met.
printf(“%d”, m);
}
}
int fun(int n)
{
int x;
x= n*n;
return x;
}
5.8 Answers for Self Assessment Questions
1. False
2. extern.
3. formal, actual
4. comma.
5. Function declaration
6. False
7. header
8. double fun(double, double);
9. Recursion.
10. True
11. 15
12. function
13. False
}
int fun(int n)
{
if(n>0) return (n+fun(n-2));
}
4. gcd(m,n)= { m or n if m=n
GCD(m, m-n) if m>n GCD(n,m) if m<n }
5. fib(i)= { 0 if i=1
1 if i=2
fib(i-1)+fib(i-2) otherwise}
6. Square of the integers from 1 to 10 is displayed.
5.10 Exercises
1. Suppose function F1 calls function F2 within a C program. Does the order
of function definitions make any difference? Explain.
2. When a program containing recursive function calls is executed, how are
the local variables within the recursive function interpreted?
3. Express the following algebraic formula in a recursive form:
Y = (Xi+X2+...+Xn)
4. Write a function that will allow a floating point number to be raised to an
integer power.
5. Write a function to swap two numbers using pass by value technique.
What is the drawback of the function?
6.1 Introduction
In the previous unit, you studied about functions. You found out that how
functions can be used to break the large problems into small problems and
then solve it. You studied how functions can be repeatedly called and used
again and again. In this unit, you will study about the types of storage classes
that are used in C. You will study how these storage classes are useful in
making the C language a very powerful computing language.
Variables are channels of communication within a program. You set a variable
to a value at one point in a program, and at another point (or points) you read
the value out again. The two points may be in adjoining statements, or they
may be in widely separated parts of the program. How long does a variable
last? How widely separated can the setting and fetching parts of the program
be, and how long after a variable is set does it persist? Depending on the
variable and how you're using it, you might want different answers to these
questions. For example, in some situations it may be desirable to introduce
certain “global” variables that are recognized throughout the entire program (or
within major portions of the program, e.g. two or more functions). Such
variables are defined differently than the usual “local” variables, which are
recognized only within a single function.
We will also consider the issue of static variables which can retain their values,
so that the function can be reentered later and the computation resumed.
Finally, we may be required to develop a large, multifunction program in terms
of several independent files, with few functions defined within each file. In such
programs, the individual functions can be defined and accessed locally within
a single file, or globally within multiple files.
Objectives:
After studying this unit, you should be able to:
• implement the concept of storage classes and visibility of variables
• explain the difference between automatic variables, global variables, static
variables and external variables.
• compile and execute a program made up of more than one source files.
could potentially have modified that variable. You would constantly be stepping
all over yourself by using a common variable name like i in two parts of your
program, and having one snippet of code accidentally overwrite the values
being used by another part of the code.
Self Assessment Questions
1. The visibility of a variable determines how much of the rest of the program
can access that variable. (True/False)
2. class refers to the persistence of a variable and its scope
within the program, that is, the portion of the program over which the
variable is recognized.
3. Visibility provides security for your data used in a program. (True/False)
n=n-1;
}
return factorial;
}
An automatic variable doesn’t retain its value once control is transferred out of
its defining function. Therefore, any value assigned to an automatic variable
within a function will be lost once the function is exited.
Self Assessment Questions
4. The scope of an automatic variable is in _____________ in which it is
declared.
5. Does an automatic variable retain its value once control is transferred out
of its defining function? (Yes/No)
6. The key word auto is must in the declaration of automatic variables.
(True/False)
while((n=linecount())>0) {
sum+=n;
++lines;
}
avg=cal_avg();
printf(“\nAverage number of characters per line: %5.2f”, avg);
}
void linecount(void)
{
/* read a line of text and count the number of characters */
char line[80];
int count=0;
while((line[count]=getchar())!=’\n’)
++count;
return count;
}
float cal_avg(void)
{
/* compute average and return*/ return (float)sum/lines;
}
In the above program the variables sum and lines are globally declared and
hence they could be used in both the functions main() and cal_avg()
Self Assessment Questions
7. The variables declared in the main() function are the global variables.
(True/False)
8. The global variables are more secured than the automatic variables in a
program. (True/False)
retain their previous values. This feature allows functions to retain information
permanently throughout the execution of a program. Static variables can be
utilized within the function in the same manner as other variables. They cannot
be accessed outside of their defining function.
In order to declare a static variable the keyword static is used as shown below:
static int count;
You can define automatic or static variables having the same name as global
variables. In such situations the local variables will take precedence over the
global variables, though the values of global variables will be unaffected by any
manipulation of the local variables.
Initial values can be included in static variable declaration. The rules
associated with the initialization remain same as the initialization of automatic
or global variables. They are:
1. The initial values must be constants, not expressions.
2. The initial values are assigned to their respective variables at the beginning
of the program execution. The variables retain these values throughout the
life of the program, unless different values are assigned during the course
of computation.
3. Zeros will be assigned to all static variables whose declarations do not
include explicit initial values.
Program 6.3: Program to generate Fibonacci numbers.
#include<stdio.h>
main()
{
int count, n;
long int fib(int);
printf(“\n How many Fibonacci numbers?”);
scanf(“%d\n”, &n);
for(count=1;count<=n;count++)
{
printf(“\ni=%d F=%ld”, count, fib(count));
}
long int fib(int count)
{
/* calculate a Fibonacci number using the formula
{
printf(“ Hi, Manipal!\n”);
return;
}
Type and save the following program in a separate source file called
demoexternfun.c
#include<stdio.h>
#include “ externfunction.h”
extern void output(void);
main()
{
output();
}
Compile and execute the above program and observe the result.
However, the keyword extern is optional in some C compilers.
Self Assessment Questions
12. The main purpose of using external variables is to access the same
variable in different _____________ files.
13. Compiler doesn’t allocate memory for an external variable where it is
accessed. (True/False)
14. Global variables and external variables have the same scope.
(True/False)
Example 6.1: Here is an example demonstrating almost everything we've seen
so far:
int globalvar = 1;
extern int anotherglobalvar;
static int privatevar;
f()
{
int localvar;
int localvar2 = 2;
static int persistentvar;
}
Here we have six variables, three declared outside and three declared inside
point (or points) you read the value out again. Thus the transfer of
information from one point of the program to another is nothing but the
communication.
6.10 Exercises
1. Distinguish between the following
i. Global and local variables
ii. Automatic and static variables
iii. Global and extern variables
2. Write a program to count the number of times a function is called using
static variables.
3. Write a function prime that returns 1 if its argument is a prime number and
returns zero Otherwise.
4. Write a function that will round a floating point number to an indicated
decimal place. For example, the number 12.456 would yield the value 12.
46 when it is rounded off to two decimal places.
5. Write a program to illustrate the concept of extern variables.
7.1 Introduction
In the previous unit, you studied about the various types of storage classes
that are used in C. You studied how those storage classes are used in different
situations in C. In this unit, you will study about the arrays and strings. You will
learn how arrays and strings are formed and manipulated.
Many applications require processing of multiple data items that have common
characteristics. In such situations it is always convenient to place the data
items into an array, where they will share the same name. An array is a
collection of similar type of elements. All elements in the array are referred with
the array name. Since arrays hold a group of data, it is very easy to perform
looping and arithmetic operations on group of data. This chapter covers the
processing of both one-dimensional and two-dimensional arrays.
Objectives:
After studying this unit, you should be able to:
• declare, initialize and process one-dimensional and two-dimensional
arrays
• explain about strings and how to process them
• describe the library functions available in C to process strings
7.2 One Dimensional Arrays
So far, we've been declaring simple variables: the declaration int i;
declares a single variable, named i, of type int. It is also possible to declare an
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]
In C, arrays are zero-based: the ten elements of a 10-element array are
numbered from 0 to 9. The subscript which specifies a single element of an
array is simply an integer expression in square brackets. The first element of
the array is a[0], the second element is a[1], etc. You can use these “array
subscript expressions'' anywhere you can use the name of a simple variable,
for example:
a[0] = 10;
a[1] = 20;
a[2] = a[0] + a[1];
Notice that the subscripted array references (i.e. expressions such as a[0] and
a[1]) can appear on either side of the assignment operator.
The subscript does not have to be a constant like 0 or 1; it can be any integral
expression. For example, it's common to loop over all elements of an array:
int i;
for(i = 0; i < 10; i = i + 1)
a[i] = 0;
This loop sets all ten elements of the array a to 0.
Arrays are a real convenience for many problems, but there is not a lot that C
will do with them for you automatically. In particular, you can neither set all
elements of an array at once nor assign one array to another; both of the
assignments
a = 0; /* WRONG */
and
int b[10];
b = a; /* WRONG */
are illegal.
To set all of the elements of an array to some value, you must do so one by
one, as in the loop example above. To copy the contents of one array to
another, you must again do so one by one:
int b[10];
return 0;
}
Program 7.2: The following program illustrates the passing of an array from
the main to a function. This program is used to find the average of n floating
point numbers.
#include<stdio.h>
main()
{
int n, i;
float avg;
float list[100];
float average(int, float[]); /* function prototype */
printf(“How many numbers:”);
scanf(“%d”,&n);
printf(“ Enter the numbers:”);
for(i=1;i<=n;i++)
scanf(“%f”, &list[i]);
avg=average(n, list); /* Here list and n are actual arguments */
printf(“Average=%f\n”, avg);
}
float average(int a, float x[ ])
{
float avg;
float sum=0;
int i;
for(i=0;i<a;i++)
sum=sum+x[i];/* find sum of all the numbers */
avg=sum/a; /* find average */
return avg;
}
c=a+b;
7.3 Multidimensional Arrays
The C language allows arrays of any dimension to be defined. In this section,
we will take a look at two-dimensional arrays. One of the most natural
applications for a two-dimensional array arises in the case of a matrix. In C,
the two-dimensional matrix can be declared as follows:
int array[3][6];
Following is the way of declaring as well as initializing two-dimensional
arrays.
int array[3][6] = {
{4,5,6,7,8,9},
{1,5,6,8,2,4},
{0,4,4,3,1,1}
};
Such arrays are accessed like so:
array[1][4]= -2;
Manipal University Jaipur Page No.: 120
Programming in C Unit 1
if (array[2][1] > 0) {
printf ("Element [2][1] is %d", array[2][1]);
}
Remember that, like ordinary arrays, two-dimensional arrays are numbered
from 0. Therefore, the array above has elements from array[0][0] to
array[2][5].
function prototypes must be written in the same manner. But while calling the
function the array name may be passed as the actual argument as in the case
of one-dimensional arrays. E.g:
void process_array (int [][6]); /* function prototype */
void process_array (int array[][6])/*function definition */ {
}
Self Assessment Questions
4. In a two-dimensional matrix, the first subscript in the declaration specifies
number of _______________________ .
5. A two-dimensional array is considered as an array of one-dimensional
arrays. (True/False)
7.4 Strings
Strings in C are represented by arrays of characters. The end of the string is
marked with a special character, the null character, which is simply the
character with the value 0. (The null character has no relation except in name
to the null pointer. In the ASCII character set, the null character is named
NULL.) The null or string-terminating character is represented by another
character escape sequence, \0.
Because C has no built-in facilities for manipulating entire arrays (copying
them, comparing them, etc.), it also has very few built-in facilities for
manipulating strings.
In fact, C's only truly built-in string-handling is that it allows us to use string
constants (also called string literals) in our code. Whenever we write a string,
enclosed in double quotes, C automatically creates an array of characters for
us, containing that string, terminated by the \0 character. For example, we can
declare and define an array of characters, and initialize it with a string constant:
char string[ ] = "Hello, world!";
In this case, we can leave out the dimension of the array, since the compiler
can compute it for us based on the size of the initializer (14, including the
terminating \0). This is the only case where the compiler sizes a string array
for us, however; in other cases, it will be necessary that we decide how big the
arrays and other data structures we use to hold strings are.
To do anything else with strings, we must typically call functions. The C library
contains a few basic string manipulation functions, and to learn more about
strings, we'll be looking at how these functions might be implemented.
Since C never lets us assign entire arrays, we use the strcpy function to copy
one string to another:
#include <string.h>
char string1[ ] = "Hello, world!";
char string2[20];
strcpy(string2, string1);
The destination string is strcpy's first argument, so that a call to strcpy mimics
an assignment expression (with the destination on the left-hand side). Notice
that we had to allocate string2 big enough to hold the string that would be
copied to it. Also, at the top of any source file where we're using the standard
library's string-handling functions (such as strcpy) we must include the line
#include <string.h>
which contains external declarations for these functions.
Since C won't let us compare entire arrays, either, we must call a function to
do that, too. The standard library's strcmp function compares two strings, and
returns 0 if they are identical, or a negative number if the first string is
alphabetically “less than'' the second string, or a positive number if the first
string is “greater.'' (Roughly speaking, what it means for one string to be “less
than'' another is that it would come first in a dictionary or telephone book,
although there are a few anomalies.) Here is an example:
char string3[] = "this is";
char string4[] = "a test";
if(strcmp(string3, string4) == 0) printf("strings are equal\n");
else printf("strings are different\n");
This code fragment will print “strings are different''. Notice that strcmp does
not return a Boolean, true/false, zero/nonzero answer, so it's not a good idea
to write something like
if(strcmp(string3, string4)) ...
because it will behave backwards from what you might reasonably expect.
(Nevertheless, if you start reading other people's code, you're likely to come
across conditionals like if(strcmp(a, b)) or even if(!strcmp(a, b)). The first does
something if the strings are unequal; the second does something if they're
Manipal University Jaipur Page No.: 123
Programming in C Unit 1
equal. You can read these more easily if you pretend for a moment that
strcmp's name were strdiff, instead.)
Another standard library function is strcat, which concatenates strings. It does
not concatenate two strings together and give you a third, new string; what it
really does is append one string onto the end of another. (If it gave you a new
string, it would have to allocate memory for it somewhere, and the standard
library string functions generally never do that for you automatically.) Here's an
example:
char string5[20] = "Hello, ";
char string6[] = "world!";
printf("%s\n", string5);
strcat(string5, string6);
printf("%s\n", string5);
The first call to printf prints “Hello, '', and the second one prints “Hello, world!'',
indicating that the contents of string6 have been tacked on to the end of string5.
Notice that we declared string5 with extra space, to make room for the
appended characters.
If you have a string and you want to know its length (perhaps so that you can
check whether it will fit in some other array you've allocated for it), you can call
strlen, which returns the length of the string (i.e. the number of characters in
it), not including the \0:
char string7[ ] = "abc";
int len = strlen(string7);
printf("%d\n", len);
Finally, you can print strings out with printf using the %s format specifier, as
we've been doing in these examples already (e.g. printf("%s\n", string5);).
Since a string is just an array of characters, all of the string-handling functions
we've just seen can be written quite simply, using no techniques more
complicated than the ones we already know. In fact, it's quite instructive to look
at how these functions might be implemented. Here is a version of strcpy:
mystrcpy(char dest[ ], char src[ ])
{
int i = 0;
while(src[i] != '\0')
{
dest[i] = src[i];
i++;
}
dest[i] = '\0';
}
We've called it mystrcpy instead of strcpy so that it won't clash with the version
that's already in the standard library. Its operation is simple: it looks at
characters in the src string one at a time, and as long as they're not \0, assigns
them, one by one, to the corresponding positions in the dest string. When it's
done, it terminates the dest string by appending a \0. (After exiting the while
loop, i is guaranteed to have a value one greater than the subscript of the last
character in src.) For comparison, here's a way of writing the same code, using
a for loop:
for(i = 0; src[i] != '\0'; i++)
dest[i] = src[i];
dest[i] = '\0';
Yet a third possibility is to move the test for the terminating \0 character out of
the for loop header and into the body of the loop, using an explicit if and break
statement, so that we can perform the test after the assignment and therefore
use the assignment inside the loop to copy the \0 to dest, too:
for(i = 0; ; i++)
{
dest[i] = src[i];
if(src[i] == '\0') break;
}
(There are in fact many, many ways to write strcpy. Many programmers like to
combine the assignment and test, using an expression like (dest[i] = src[i]) !=
'\0')
while(1)
{
if(str1[i] != str2[i])
return str1[i] - str2[i];
if(str1[i] == '\0' || str2[i] == '\0')
return 0;
i++; } }
Characters are compared one at a time. If two characters in one position differ,
the strings are different, and we are supposed to return a value less than zero
if the first string (str1) is alphabetically less than the second string. Since
characters in C are represented by their numeric character set values, and
since most reasonable character sets assign values to characters in
alphabetical order, we can simply subtract the two differing characters from
each other: the expression str1[i] - str2[i] will yield a negative result if the i'th
character of str1 is less than the corresponding character in str2. (As it turns
out, this will behave a bit strangely when comparing upper and lower-case
letters, but it's the traditional approach, which the standard versions of strcmp
tend to use.) If the characters are the same, we continue around the loop,
unless the characters we just compared were (both) \0, in which case we've
reached the end of both strings, and they were both equal. Notice that we used
what may at first appear to be an infinite loop--the controlling expression is the
constant 1, which is always true. What actually happens is that the loop runs
until one of the two return statements breaks out of it (and the entire function).
Note also that when one string is longer than the other, the first test will notice
this (because one string will contain a real character at the [i] location, while
the other will contain \0, and these are not equal) and the return value will be
computed by subtracting the real character's value from 0, or vice versa. (Thus
the shorter string will be treated as “less than'' the longer.)
}
In this case, all we have to do is find the \0 that terminates the string, and it
turns out that the three control expressions of the for loop do all the work;
there's nothing left to do in the body. Therefore, we use an empty pair of braces
{ } as the loop body. Equivalently, we could use a null statement, which is
simply a semicolon:
Everything we've looked at so far has come out of C's standard libraries. As
one last example, let's write a substr function, for extracting a substring out of
a larger string. We might call it like this:
and integers.
As we have had several occasions to mention, a character is represented
internally as a small integer, with a value depending on the character set in
use. For example, we might find that 'A' had the value 65, that 'a' had the value
97 and that '+' had the value 43. (These are, in fact, the values in the ASCII
character set, which most computers use. However, you don't need to learn
these values, because the vast majority of the time, you use character
constants to refer to characters, and the compiler worries about the values for
you. Using character constants in preference to raw numeric values also
makes your programs more portable.)
As we may also have mentioned, there is a big difference between a character
and a string, even a string which contains only one character (other than the
\0). For example, 'A' is not the same as "A". To drive home this point, let's
illustrate it with a few examples.
If you have a string:
char string[ ] = "hello, world!";
you can modify its first character by saying
string[0] = 'H';
(Of course, there's nothing magic about the first character; you can modify any
character in the string in this way. Be aware, though, that it is not always safe
to modify strings in-place like this) Since you're replacing a character, you want
a character constant, 'H'. It would not be right to write
string[0] = "H"; /* WRONG */
because "H" is a string (an array of characters), not a single character. (The
destination of the assignment, string[0], is a char, but the right-hand side is a
string; these types don't match.)
On the other hand, when you need a string, you must use a string. To print a
single newline, you could call
printf("\n");
It would not be correct to call
printf('\n'); /* WRONG */
printf always wants a string as its first argument. (As one final example, putchar
8. The library function atoi can be used for any string. (True/False)
7.5 Summary
An array is a variable that can hold more than one value. In C, arrays are zero-
based. An array name can be used as an argument to a function, thus
permitting the entire array to be passed to the function. The C language allows
arrays of any dimension to be defined. One of the most natural applications for
a two-dimensional array arises in the case of a matrix. Strings in C are
represented by arrays of characters. C has built in library functions to perform
some operations on strings.
i++;
}
printf(“Sum=%d”, sum);
}
2. 60
3. Yes, It is correct.
4. Statement is False.
5. // loops for array processing
#include <stdio.h>
#define SIZE 10
#define PAR 72
int main(void)
{
int index, score[SIZE];
int sum = 0;
float average;
printf("Enter %d golf scores:\n", SIZE);
for (index = 0; index < SIZE; index++)
scanf("%d", &score[index]); */read in the ten scores printf("The
scores read in are as follows:\n");
for (index = 0; index < SIZE; index++)
printf("%5d", score[index]); */verify input
printf("\n");
for (index = 0; index < SIZE; index++)
sum += score[index]; */add them up
average = (float) sum / SIZE; */ time-honored method
printf("Sum of scores = %d, average = %.2f\n", sum, average);
printf("That's a handicap of %.0f.\n", average - PAR);
return 0;
}
7.9 Exercises
1. Write a program to count the number of vowels and consonants in a given
string.
2. Write a program to arrange a list of numbers in ascending order.
Unit 8 Pointers - I
Structure:
8.1 Introduction
Objectives
8.2 Basics of Pointers
8.3 Pointers and One-dimensional Arrays
Pointer Arithmetic
Pointer Subtraction and Comparison
Similarities between Pointers and One-dimensional Arrays
8.4 Summary
8.5 Terminal Questions
8.6 Answers to Self AssessmentQuestions
8.7 Answers to TerminalQuestions
8.8 Exercises
8.1 Introduction
In the previous unit, you studied about the arrays and strings. You studied how
the arrays and strings are declared and manipulated in making C a more easy
and powerful programming language. In this unit, you will study about pointer
which is another useful topic that makes C a most powerful programming
language. You will study about the basics of pointer and pointer arithmetic.
A pointer is a variable that points at, or refers to, another variable. That is, if
we have a pointer variable of type “pointer to int, “it might point to the int
variable i, or to any one of the locations of the int array a. Given a pointer
variable, we can ask questions like, “what’s the value of the variable that this
pointer points to?”.
Why would we want to have a variable that refers to another variable? Why not
just use that other variable directly? Pointers are used frequently in C, as they
have number of useful applications. For example, pointers can be used to pass
information back and forth between a function and its reference point. In
particular, pointers provide a way to return multiple data items from a function
via function arguments. Pointers also permit references to other functions to
be specified as arguments to a given function. This has the effect of passing
functions as arguments to the given function.
Pointers are also closely associated with the arrays and therefore provide an
alternate way to access individual array elements. Pointers make the program
efficient when we use pointers for accessing the individual array elements. In
this unit, you will study about the basics of pointers and its usage in one
dimensional array.
Objectives:
After studying this unit, you should be able to:
• implement pointers in your program
• write a program related to arrays and using a pointer with it
• solve and illustrate the use of pointer arithmetics in C
• point out the similarities between pointers and one dimensional arrays
careful when we're saying it) that a pointer variable has a value, and that its
value is “pointer to that other variable”. This will make more sense when we
see how to generate pointer values.
Pointers (that is, pointer values) are generated with the “address-of” operator
&, which we can also think of as the “pointer-to” operator. We demonstrate this
by declaring (and initializing) an int variable i, and then setting ip to point to it:
int i = 5;
ip = &i;
The assignment expression ip = &i; contains both parts of the “two-step
process”: &i generates a pointer to i, and the assignment operator assigns the
new pointer to (that is, places it “in”) the variable ip. Now ip “points to” i, which
we can illustrate with this picture:
ip2 = ip;
and
* ip2 = *ip;
do two very different things. The first would make ip2 again point to where ip
points (in other words, back to i again). The second would store, at the location
pointed to by ip2, a copy of the value pointed to by ip; in other words (if ip and
ip2 still point to i and j respectively) it would set j to i's value, or 7.
It's important to keep very clear in your mind the distinction between a pointer
and what it points to. You can't mix them. You can't “set ip to 5” by writing
something like
ip = 5; /* WRONG */
5 is an integer, but ip is a pointer. You probably wanted to “set the value pointed
to by ip to 5,” which you express by writing
* ip = 5;
Similarly, you can't “see what ip is” by writing
printf("%d\n", ip); /* WRONG */
Again, ip is a pointer-to-int, but %d expects an int. To print what ip points to,
use
printf("%d\n", *ip);
Finally, a few more notes about pointer declarations. The * in a pointer
declaration is related to, but different from, the contents-of operator *. After we
declare a pointer variable
int *ip;
the expression
ip = &i
sets what ip points to (that is, which location it points to), while the expression
* ip = 5
sets the value of the location pointed to by ip. On the other hand, if we declare
a pointer variable and include an initializer:
int *ip3 = &i;
we're setting the initial value for ip3, which is where ip3 will point, so that initial
value is a pointer. (In other words, the * in the declaration int *ip3 = &i; is not
the contents-of operator, it's the indicator that ip3 is a pointer.)
If you have a pointer declaration containing an initialization, and you ever have
occasion to break it up into a simple declaration and a conventional
assignment, do it like this:
int *ip3;
ip3 = &i;
Don't write
int *ip3;
* ip3 = &i;
or you'll be trying to mix pointer and the value to which it points
Also, when we write int *ip;
although the asterisk affects ip's type, it goes with the identifier name ip, not
with the type int on the left. To declare two pointers at once, the declaration
looks like
int *ip1, *ip2;
Some people write pointer declarations like this:
int* ip;
This works for one pointer, because C essentially ignores whitespace. But if
you ever write
int* ip1, ip2; /* PROBABLY WRONG */
it will declare one pointer-to-int ip1 and one plain int ip2, which is probably not
what you meant.
What is all of this good for? If it was just for changing variables like i from 5 to
7, it would not be good for much. What it's good for, among other things, is
when for various reasons we don't know exactly which variable we want to
change.
Program 8.1: A simple program to illustrate the relationship between
two integer variables, their corresponding addresses and their
associated pointers.
#include<stdio.h>
main()
Manipal University Jaipur Page No.: 139
Programming in C Unit 1
{
int x=5;
int y;
int *px; /* pointer to an integer */
int *py; /* pointer to an integer */
px=&x; /* assign address of x to px */
y=*px; /* assign value of x to y */
py=&y; /* assign address of y to py */
printf(“\nx=%d &x=%u px=%u *px=%d”, x, &x, px, *px); printf(“\ny=%d
&y=%u py=%u *py=%d”, y, &y, py, *py);
}
Execute this program and observe the result.
We'd use this ip just like the one in the previous section: *ip gives us what ip
points to, which in this case will be the value in a[3].
8.3.1 Pointer Arithmetic
Once we have a pointer pointing into an array, we can start doing pointer
arithmetic. Given that ip is a pointer to a[3], we can add 1 to ip:
ip + 1
What does it mean to add one to a pointer? In C, it gives a pointer to the cell
one farther on, which in this case is a[4]. To make this clear, let's assign this
new pointer to another pointer variable:
ip2 = ip + 1;
Now the picture looks like this:
If we now do
* ip2 = 4;
we've set a[4] to 4. But it's not necessary to assign a new pointer value to a
pointer variable in order to use it; we could also compute a new pointer value
and use it immediately:
* (ip + 1) = 5;
In this last example, we've changed a[4] again, setting it to 5. The
parentheses are needed because the unary “contents of'' operator * has
higher precedence (i.e., binds more tightly than) the addition operator. If we
wrote *ip + 1, without the parentheses, we'd be fetching the value pointed to
by ip, and adding 1 to that value. The expression *(ip + 1), on the other hand,
accesses the value one past the one pointed to by ip.
Given that we can add 1 to a pointer, it's not surprising that we can add and
subtract other numbers as well. If ip still points to a[3], then
* (ip + 3) = 7;
sets a[6] to 7, and
* (ip - 2) = 4;
sets a[1] to 4.
Up above, we added 1 to ip and assigned the new pointer to ip2, but there's
no reason we can't add one to a pointer, and change the same pointer:
* p = ip + 1;
Now ip points one past where it used to (to a[4], if we hadn't changed it in the
meantime). The shortcuts work for pointers, too: we could also increment a
pointer using
* p += 1;
or
ip++;
Of course, pointers are not limited to ints. It's quite common to use pointers to
other types, especially char.
Example 8.1: Here is a program segment to compare two strings, character
by character using pointers.
char *p1 = &str1[0], *p2 = &str2[0];
while(1)
{
if(*p1 != *p2)
return *p1 - *p2;
if(*p1 == '\0' || *p2 == '\0')
return 0;
p1++;
p2++; }
The auto increment operator ++ (like its companion, --) makes it easy to do two
things at once. We've seen idioms like a[i++] which accesses a[i] and
simultaneously increments i, leaving it referencing the next cell of the array a.
We can do the same thing with pointers: an expression like *ip++ lets us access
what ip points to, while simultaneously incrementing ip so that it points to the
next element. The preincrement form works, too: *++ip increments ip, then
accesses what it points to. Similarly, we can use notations like *ip-- and *--ip.
pointer:
char *dp = &dest[0], *sp = &src[0];
while(*sp != '\0')
*dp++ = *sp++;
*dp = '\0';
(One question that comes up is whether the expression *p++ increments p or
what it points to. The answer is that it increments p. To increment what p points
to, you can use (*p)++.)
When you're doing pointer arithmetic, you have to remember how big the array
the pointer points into is, so that you don't ever point outside it. If the array a
has 10 elements, you can't access a[50] or a[-1] or even a[10] (remember, the
valid subscripts for a 10-element array run from 0 to 9). Similarly, if a has 10
elements and ip points to a[3], you can't compute or access ip + 10 or ip - 5.
(There is one special case: you can, in this case, compute, but not access, a
pointer to the nonexistent element just beyond the end of the array, which in
this case is &a[10]. This becomes useful when you're doing pointer
comparisons, which we'll look at next.)
ip2 = ip1 + 3;
and the answer is, yes. When you subtract two pointers, as long as they point
into the same array, the result is the number of elements separating them. You
can also ask (again, as long as they point into the same array) whether one
pointer is greater or less than another: one pointer is “greater than” another if
it points beyond where the other one points. You can also compare pointers
for equality and inequality: two pointers are equal if they point to the same
variable or to the same cell in an array, and are (obviously) unequal if they
don't. (When testing for equality or inequality, the two pointers do not have to
point into the same array.)
One common use of pointer comparisons is when copying arrays using
pointers.
Example 8.3: Here is a code fragment which copies 10 elements from array1
to array2, using pointers. It uses an end pointer, endptr, to keep track of
when it should stop copying.
int array1[10], array2[10];
int *ip1, *ip2 = &array2[0];
int *endptr = &array1[10];
}
Execute this program and observe the result.
8.3.3 Similarities between Pointers and One-dimensional Arrays
There are a number of similarities between arrays and pointers in C. If you
have an array
int a[10];
you can refer to a[0], a[1], a[2], etc., or to a[i] where i is an int. If you declare a
pointer variable ip and set it to point to the beginning of an array:
int *ip = &a[0];
you can refer to *ip, *(ip+1), *(ip+2), etc., or to *(ip+i) where i is an int.
There are also differences, of course. You cannot assign two arrays; the code
int a[10], b[10];
a = b; /* WRONG */
is illegal. As we've seen, though, you can assign two pointer variables:
int *ip1, *ip2;
ip1 = &a[0];
ip2 = ip1;
Pointer assignment is straightforward; the pointer on the left is simply made to
point wherever the pointer on the right does. We haven't copied the data
pointed to (there's still just one copy, in the same place); we've just made two
pointers point to that one place.
The similarities between arrays and pointers end up being quite useful, and in
fact C builds on the similarities, leading to what is called “the equivalence of
arrays and pointers in C.'' When we speak of this “equivalence'' we do not
mean that arrays and pointers are the same thing (they are in fact quite
different), but rather that they can be used in related ways, and that certain
operations may be used between them.
The first such operation is that it is possible to (apparently) assign an array to
a pointer:
int a[10];
int *ip;
ip = a;
What can this mean? In that last assignment ip = a, C defines the result of this
assignment to be that ip receives a pointer to the first element of a. In other
words, it is as if you had written
ip = &a[0];
The second facet of the equivalence is that you can use the “array subscripting''
notation [i] on pointers, too. If you write ip[3]
it is just as if you had written
*(ip + 3)
So when you have a pointer that points to a block of memory, such as an array
or a part of an array, you can treat that pointer “as if'' it were an array, using
the convenient [i] notation. In other words, at the beginning of this section when
we talked about *ip, *(ip+1), *(ip+2), and *(ip+i), we could have written ip[0],
ip[1], ip[2], and ip[i]. As we'll see, this can be quite useful (or at least
convenient).
The third facet of the equivalence (which is actually a more general version of
the first one we mentioned) is that whenever you mention the name of an array
in a context where the “value'' of the array would be needed, C automatically
generates a pointer to the first element of the array, as if you had written
&array[0]. When you write something like
int a[10];
int *ip;
ip = a + 3;
it is as if you had written ip = &a[0] + 3;
which (and you might like to convince yourself of this) gives the same result as
if you had written
ip = &a[3];
For example, if the character array
char string[100];
contains some string, here is another way to find its length:
int len;
char *p;
for(p = string; *p != '\0'; p++)
;
Manipal University Jaipur Page No.: 146
Programming in C Unit 1
len = p - string;
After the loop, p points to the '\0' terminating the string. The expression p -
string is equivalent to p - &string[0], and gives the length of the string. (Of
course, we could also call strlen; in fact here we've essentially written another
implementation of strlen.)
Self Assessment Questions
5. You can perform any type of arithmetic operation on pointers. (True or
False)
6. For an int array a[10], If you declare a pointer variable ip then you can set
it to point to the beginning of an array by assigning: int *ip =
8.4 Summary
A pointer is a variable that points at, or refers to, another variable. The general
format of a simple pointer declaration is: datatype *variablename. We discover
the value pointed to by a pointer using the “contents-of” operator, *. Pointers
are very useful when you want to refer to some other variable by pointing at it.
Pointers not only point to single variables but can also point at the cells of an
array. Pointers are also closely associated with the arrays and therefore
provide an alternate way to access individual array elements. Once we have a
pointer pointing into an array, we can do some pointer arithmetic-add a
constant to a variable or subtract a constant from a pointer, or you can subtract
two pointers. There are similarities between pointers and one dimension arrays
in some aspects although they are entirely different concepts and there are
differences also in other aspects.
8.8 Exercise
1. Write a program that uses the pointer arithmetic.
2. Write a program that can accept any 5 data and sort it in ascending order
using pointers.
3. With the help of pointers, write a program that uses functions to swap the
data.
4. What are the various operators that are used when implementing a pointer
in a program? Explain with an example.
Unit 9 Pointers - II
Structure:
9.1 Introduction
Objectives
9.2 Null pointers
9.3 Pointers as FunctionArguments
9.4 Pointers and Strings
9.5 Pointers and two-dimensionalarrays
Arrays of Pointers
9.6 Summary
9.7 Terminal Questions
9.8 Answers to Self Assessment Questions
9.9 Answers for TerminalQuestions
9.10 Exercises
9.1 Introduction
In the previous unit, you read about the basics of pointers. A pointer is a
variable that points at, or refers to, another variable. Pointers are very useful
when you want to refer to some other variable by pointing at it. Pointers not
only point to single variables but can also point at the cells of an array. Pointers
provide a convenient way to represent multidimensional arrays, allowing a
single multidimensional array to be replaced by a lowerdimensional array of
pointers. This feature permits a collection of strings to be represented within a
single array, even though the individual strings may differ in length.
In this unit, you will read about null pointers and how pointers can be used to
manipulate a string along with the usage of pointers in two dimensional arrays.
You will also read about arrays of pointers briefly.
Objectives:
After studying this unit, you should be able to:
• explain a null pointer
• use pointers as function arguments ?
• manipulate strings by the help of pointers
• explain arrays of pointers
9.2 Null Pointers
We said that the value of a pointer variable is a pointer to some other variable.
There is one other value a pointer may have: it may be set to a null pointer. A
null pointer is a special pointer value that is known not to point anywhere. What
this means that no other valid pointer, to any other variable or array cell or
for the pattern starting at start, so we return start. Otherwise, we go around the
outer loop again, to try another starting position. If we run out of those (if *start
== '\0'), without finding a match, we return a null pointer.
Notice that the function is declared as returning (and does in fact return) a
pointer-to-char.
In general, C does not initialize pointers to null for you, and it never tests
pointers to see if they are null before using them. If one of the pointers in your
programs points somewhere some of the time but not all of the time, an
excellent convention to use is to set it to a null pointer when it doesn't point
anywhere valid, and to test to see if it's a null pointer before using it. But you
must use explicit code to set it to NULL, and to test it against NULL. (In other
words, just setting an unused pointer variable to NULL doesn't guarantee
safety; you also have to check for the null value before using the pointer.) On
the other hand, if you know that a particular pointer variable is always valid,
you don't have to insert a paranoid test against NULL before using it.
Self Assessment Questions
1. A _____ is a special pointer value that is known not to point anywhere.
2. A function that returns ___________ values can return a null pointer
when it is unable to perform its task.
3. In general, C does not initialize pointers to null for you, and it never tests
pointers to see if they are null before using them. (True/False)
parameter passing, the caller can not get the changes done in the function.
This can be achieved using pointers as illustrated in the following program.
Program 9.2: Program to swap two integers using pointers
#include<stdio.h>
main()
{
int a, b;
void swap(int *a, int *b);
printf(“ Read the integers:”);
scanf(“%d%d”, &a, &b);
swap(&a, &b); /* call by reference or call by address*/
printf(“ \nAfter swapping:a=%d b=%d”, a, b);
}
void swap(int *x, int *y)
{
int temp;
temp=*x;
*x=*y;
*y=temp;
return;
}
Execute this program and observe the result.
Because of the use of call by reference, the changes made in the function
swap() are also available in the main().
9.4 Pointers and Strings
Because of the similarity of arrays and pointers, it is extremely common to refer
to and manipulate strings as character pointers, or char *'s. It is so common, in
fact, that it is easy to forget that strings are arrays, and to imagine that they're
represented by pointers. (Actually, in the case of strings, it may not even matter
that much if the distinction gets a little blurred; there's certainly nothing wrong
with referring to a character pointer, suitably initialized, as a ’’string.'') Let's look
at a few of the implications:
Any function that manipulates a string will actually accept it as a char *
argument. The caller may pass an array containing a string, but the function
will receive a pointer to the array's (string's) first element (character).
The %s format in printf expects a character pointer.
Manipal University Jaipur Page No.: 154
Programming in C Unit 1
Although you have to use strcpy to copy a string from one array to another,
you can use simple pointer assignment to assign a string to a pointer. The
string being assigned might either be in an array or pointed to by another
pointer. In other words, given
char string[] = "Hello, world!";
char *p1, *p2;
both
p1 = string and
p2 = p1
are legal. (Remember, though, that when you assign a pointer, you're making
a copy of the pointer but not of the data it points to. In the first example, p1
ends up pointing to the string in string. In the second example, p2 ends up
pointing to the same string as p1. In any case, after a pointer assignment, if
you ever change the string (or other data) pointed to, the change is “visible'' to
both pointers.
Many programs manipulate strings exclusively using character pointers, never
explicitly declaring any actual arrays. As long as these programs are careful to
allocate appropriate memory for the strings, they're perfectly valid and correct.
When you start working heavily with strings, however, you have to be aware of
one subtle fact.
When you initialize a character array with a string constant:
char string[] = "Hello, world!";
you end up with an array containing the string, and you can modify the array's
contents to your heart's content:
string[0] = 'J';
However, it's possible to use string constants (the formal term is string literals)
at other places in your code. Since they're arrays, the compiler generates
pointers to their first elements when they're used in expressions, as usual. That
is, if you say
char *p1 = "Hello";
int len = strlen("world");
it's almost as if you'd said
char internal_string_1[] = "Hello";
Manipal University Jaipur Page No.: 155
Programming in C Unit 1
where line is the line we're breaking into words, words is the array to be filled
in with the (pointers to the) words, and nwords (the return value from getwords)
is the number of words which the function finds. (As with getline, we tell the
function the size of the array so that if the line should happen to contain more
words than that, it won't overflow the array).
Here is the definition of the getwords function. It finds the beginning of each
word, places a pointer to it in the array, finds the end of that word (which is
signified by at least one whitespace character) and terminates the word by
placing a '\0' character after it. (The '\0' character will overwrite the first
whitespace character following the word.) Note that the original input string is
therefore modified by getwords: if you were to try to print the input line after
calling getwords, it would appear to contain only its first word (because of the
first inserted '\0').
#include <stddef.h>
#include <ctype.h>
getwords(char *line, char *words[], int maxwords) {
char *p = line;
int nwords = 0;
while(1)
{
while(isspace(*p)) p++;
if(*p == '\0')
return nwords;
*p++ = '\0';
}
Each time through the outer while loop, the function tries to find another word.
First it skips over whitespace (which might be leading spaces on the line, or
the space(s) separating this word from the previous one). The isspace function
is new: it's in the standard library, declared in the header file <ctype.h>, and it
returns nonzero (“true'') if the character you hand it is a space character (a
space or a tab, or any other whitespace character there might happen to be).
When the function finds a non-whitespace character, it has found the beginning
of another word, so it places the pointer to that character in the next cell of the
words array. Then it steps through the word, looking at nonwhitespace
characters, until it finds another whitespace character, or the \0 at the end of
the line. If it finds the \0, it's done with the entire line; otherwise, it changes the
whitespace character to a \0, to terminate the word it's just found, and
continues. (If it's found as many words as will fit in the words array, it returns
prematurely.)
Each time it finds a word, the function increments the number of words
(nwords) it has found. Since arrays in C start at [0], the number of words the
function has found so far is also the index of the cell in the words array where
the next word should be stored. The function actually assigns the next word
and increments nwords in one expression:
words[nwords++] = p;
You should convince yourself that this arrangement works, and that (in this
case) the preincrement form
words[++nwords] = p; /* WRONG */
would not behave as desired.
When the function is done (when it finds the \0 terminating the input line, or
when it runs out of cells in the words array) it returns the number of words it
has found.
Here is a complete example of calling getwords:
char line[] = "this is a test";
int i;
nwords = getwords(line, words, 10);
where data-type is the type of array elements and expression is the positive-
valued integer expression that indicates the number of rows.
Example 9.6: Suppose that x is a two-dimensional array having 10 rows and
20 columns, we can define x as a one-dimensional array of pointers by writing
int *x[10];
Hence, x[0] points to the beginning of the first row, x[1] points to the beginning
of the second row and so on.
An individual array element, such as x[2][4] can be accessed
by writing
*(x[2]+4)
In this expression, x[2] is a pointer to the first element in row 2, so that (x[2]+5)
points to element 4(actually, the fifth element) within row 2. The element of this
pointer, *(x[2]+4), therefore refers to x[2][4].
9.6 Summary
A pointer is a variable that represents the location of a data item, such as a
variable or an array element. A pointer may be set to a null pointer. A null
pointer is a special pointer value that is known not to point anywhere. Passing
pointers as arguments to functions is called pass by reference. Because of the
similarity of arrays and pointers, it is extremely common to refer to and
manipulate strings as character pointers. We can define a two dimensional
array as a pointer to a group of contiguous one-dimensional arrays. A two-
dimensional array can also be expressed in terms of an array of pointers rather
than as a pointer to a group of contiguous arrays.
3. Distinguish between pass by value and pass by reference with the help of
an example.
4. How can you manipulate strings using character pointers? Illustrate it with
the help of an example.
9.8 Answers to Self Assessment Questions
1. null pointer
2. pointer
3. True
4. string
5. pointers
6. null
7. one
8. array
9.10 Exercises
1. Write a program to find the number of characters in a string using pointers.
2. Write a program to multiply two matrices using pointers.
3. Suppose a formal argument within a function definition is a pointer to
another function. How is the formal argument declared? Within the formal
argument declaration, to what does the data type refer?
4. Write a program to concatenate two strings.
5. Write a function to sort an array of numbers using pointers.
10.1 Introduction
In the previous units, you studied about the pointers as a most powerful tool in
C programming language. It is due to the pointer only that makes C as the most
beautiful language. In this unit, you will study another useful ways of making
the program easy and efficient. This unit will enable you to learn about the
structures and unions.
As we know an array is a data structure whose elements are all of the same
data type. We now turn our attention to the structure, which is a data structure
whose individual elements can differ in type. Thus, a single structure might
contain integer elements, floating-point elements and character elements.
Pointers, arrays and other structures can also be included as elements within
a structure.
This unit is concerned with the use of structures within a C program. We will
see how structures are defined, and how their individual members are
accessed and processed within a program.
Closely associated with the structure is the union, which also contains multiple
members. Unlike a structure, however, the members of a union share the same
storage area, even though the individual members may differ in type.
Objectives:
After studying this unit, you should be able to:
• handle a group of logically related data items known as structures.
• declare an array of structures, each element of the array representing a
structure variable.
• pass structure as an argument to functions and return structure from
functions.
• refer to (i.e., point to) an incomplete type, including itself.
• handle a group of logically related data items in terms of unions.
pages integer
price float
We can declare structure variables using the tag name anywhere in the
program. e.g, the statement:
struct book_bank book1, book2, book3;
declares book1, book2 and book3 as variables of type book_bank.
Each one of these variables has four members as specified by the template.
The complete declaration might look like this:
struct book_bank
{
char title[20];
char author[15];
int pages;
float price;
};
struct book_bank book1, book2, book3;
It is also allowed to combine both the template declaration and variables
declaration in one statement.
struct book_bank
{
char title[20];
char author[15];
int pages;
float price;
} book1, book2, book3;
General format of a Structure Definition:
The general format of a structure definition is as follows: struct tag_name {
data_type member1;
data_type member2;
};
In defining a structure you may note the following syntax:
1. The template is terminated with a semicolon.
2. While the entire declaration is considered as a statement, each member is
declared independently for its name and type in a separate statement
inside the template.
3. The tag name such as tag_name can be used to declare structure variables
of its type, later in the program.
Giving values to Members:
Structure members need to be linked to the structure variables in order to
make them meaningful members. The link between a member and a variable
is established using the member operator ‘.’ which is also known as ‘dot
operator’ or ‘period operator’.
Here is how we would assign values to the members of book1.
strcpy(book1.title,”BASIC”);
strcpy(book1.author,”Balagurusamy”);
book1.pages = 250;
book1.price = 28.50;
We can also give the values through the keyboard.
gets(book1.title);
gets(book1.author);
printf(“%d”,book1.pages);
printf(“%f”,book1.price);
Structure Initialization:
void main( )
{
struct st_record
{
char name[20];
int weight;
float height;
};
static struct st_record studentl = {“Suresh”, 60, 180.75};
static struct st_record student2 = {“Umesh”, 53, 170.60}; }
#include<conio.h>
#include<stdio.h>
struct personal {
char name[30];
int day;
int month;
int year;
float salary;
};
void main()
{
struct personal p;
printf(“Enter the name:\n)";
gets(p.name);
printf(“Enter the day of joining:\n)";
scanf(“%d”,&p.day);
printf(“Enter the month of joining:\n");
scanf(“%d”,&p.month);
printf(“Enter the year of joining:\n)";
scanf(“%d”,&p.year);
printf(“Enter the salary:\n)";
scanf(“%f”, & p.salary);
printf(“\nName:",p.name);
printf("\nDate of joining:%d %d %d",p.day,p.month,p.year);
printf(“Salary:",p.salary);
getch();
}
Comparison of structure variables
Two variables of same structure type can be compared in the same way as
ordinary variables. If person1 and person2 belong to the same structure, then
the following operations are valid:
Operation Meaning
void main()
{
int x;
static struct stclass studentl = {111,"Rao",72.50};
static struct stclass student2 = {222,"Reddy",67.00};
struct stclass student3;
student3 = student2;
x=((student3.number == student2.number) && (student3.marks ==
student2.marks))? 1:0;
if(x==1)
{
printf("\nStudent2 and Student3 are same ");
printf(“ %d\t %s\t %f\t“,student3.number,student3.name,student3.marks);
} else { printf("\nStudent2 and student3 are different)";
}
getch();
}
{
name char[30];
marks float;
};
main ( )
{
struct student studentl;
studentl = read_student ( );
print_student( studentl);
read_student_p(student1);
print_student (studentl);
}
struct student read_student( )
{
struct student student2;
gets(student2.name);
scanf("%d",&student2.marks);
return (student2);
}
void print_student (struct student student2)
{
printf( "name is %s\n", student2.name);
printf( "marks are%d\n", student2.marks);
}
void read_student_p(struct student student2)
{
gets(student2.name);
scanf("%d",&student2.marks);
}
Explanation
1. The function read_student reads values in structures and returns the
structure.
2. The function print_student takes the structure variable as input and prints
the content in the structure.
3. The function read_student_p reads the data in the structure similarly to
read_student. It takes the structure student as an argument and puts the
data in the structure. Since the data of a member of the structure is
modified, you need not pass the structure as a pointer even though
structure members are modified. Here you are not modifying the structure,
but you are modifying the structure members through the structure.
main( )
{
static struct marks student[3]={{45,68,81},{75,53,69},{57,36,71}};
}
This declares the student as an array of three elements student[0], student[1]
and student[2] and initializes their members as follows: student[0].subject1 =
45;
student[0].subject2 = 68;
student[2].subject3 = 71;
Program 10.4: To process employee details using structures
#include<conio.h>
#include<stdio.h>
struct employee
{
int empno;
Manipal University Jaipur Page No.: 170
Programming in C Unit 1
char name[30];
int basic;
int hra;
};
void main()
{
int i,j,n,net[50];
float avg;
employee e[50];
printf("\nEnter the number of employees:");
scanf(“%d”, &n);
printf(“\nEnter Empno.\tName\tBasic\tHra of each employee:\n");
for(i=0;i<n;i++)
{
scanf(“%d”,&e[i].empno);
gets(e[i].name);
scanf(“%d”,&e[i].basic);
scanf(%d”,&e[i].hra);
net[i]= e[i].basic+e[i].hra;
avg=avg+net[i];
}
avg=avg/n;
printf("\nEmpno.\tName\tNetpay\n");
for(i=0;i<n;i++)
{
if(net[i]>avg)
{
printf(e[i].empno\t)";
printf(e[i].name\t)";
printf(net[i]\n");
}
}
getch();
}
Program 10.5: To process student details using structures
#include<conio.h>
#include<stdio.h>
struct student
Manipal University Jaipur Page No.: 171
Programming in C Unit 1
{
int rollno;
char name[30];
int marks1;
int marks2;
int marks3;
};
void main()
{
int i,j,n,tot[50],t;
student s[50],temp;
printf("\nEnter the number of students:");
scanf(“%d”,&n);
printf("\nEnter Rollno.\tName\tMarks1\tMarks2\tMarks3 of each student:\n");
for(i=0;i<n;i++)
{
scanf(“%d”,&s[i].rollno);
gets(s[i].name);
scanf(“%d”,&s[i].marks1);
scanf(“%d”,&s[i].marks2);
scanf(“%d”,&s[i].marks3);
tot[i]= s[i].marks1+s[i].marks2+s[i].marks3;
}
for(i=0;i<n-1;i++)
{
for(j=i+1;j<n;j++)
{
if(tot[i]<tot[j])
{
temp=s[i];
s[i]=s[j];
s[j]=temp;
t=tot[i];
tot[i]=tot[j];
tot[j]=t;
}
}
}
Manipal University Jaipur Page No.: 172
Programming in C Unit 1
(*pp).x = 100;
10.7 Unions
Unions look similar to structures. They have identical declaration syntax and
member access, but they serve a very different purpose.
union Utype {
int ival;
float fval;
char *sval;
Manipal University Jaipur Page No.: 174
Programming in C Unit 1
};
union Utype x, y, z;
Accessing members of a union is via “.” member operator or, for pointers to
unions, the -> operator.
A union holds the value of one-variable at a time. The compiler allocates
storage for the biggest member of the union. The type retrieved from the union
must be the type most recently stored. Otherwise, the result is implementation
dependent.
union Utype x;
x.fval = 56.4; /* x holds type float. */
printf("%f\n", x.fval); /* OK. */
printf("%d\n", x.ival); /* Implementation dependent. */
Unions are used to store one of a set of different types. These are commonly
used to implement a “variant” array. (This is a form of generic programming.)
There are other uses also, but they are quite advanced (e.g., concern the
alignment properties of unions).
Self Assessment Questions
13. A _________ holds the value of one-variable at a time.
14. The compiler allocates storage for the smallest member of the union.
(True/False)
10.8 Summary
A structure is a convenient tool for handling a group of logically related data
items. Structure members need to be linked to the structure variables in order
to make them meaningful members. We can write programs with structures by
using modular programming. We can use structures to describe the format of
a number of related variables. Passing a pointer to a structure is generally
much more efficient than making a copy of the structure itself. The ability to
refer to (i.e., point to) an incomplete type, including itself, is an important
property for constructing a variety of data- structures.
Unions have identical declaration syntax and member access, but they serve
a very different purpose. A union holds the value of one-variable at a time. The
compiler allocates storage for the biggest member of the union.
double d;
} u;
printf(“%d\n”, sizeof(u));
u.i= 100;
printf(“%d %f %f\n”, u.i, u.f, u.d);
u.f=0.5;
printf(“%d %f %f\n”, u.i, u.f, u.d);
u.d = 0.0166667;
printf(“%d %f %f\n”, u.i, u.f, u.d); }
line, only the second value (0.500000) is meaningful. In the last line, only the
last value (0.016667) is meaningful.
10.12 Exercises
1. What is a structure? How does a structure differ from an array?
2. What is a member? What is the relationship between a member and a
structure?
3. Describe what is wrong in the following structure declaration: struct
{
int number;
float price;
}
main()
{
}
4. Describe Array of structures with an example program.
5. Define a structure called cricket that will describe the following information:
(1) player name (ii) team name (iii) batting average
Using cricket, declare an array player with 50 elements and write a
program to read the information about all the 50 players and print a team-
wise list containing names of players and print a team-wise list containing
names of players with their batting average.
6. How is a structure type pointer variable declared?
7. Write a program to find the number of characters in a string using
pointers.
11.1 Introduction
In the previous unit, you studied about the structures and unions in C. You
studied how those structures and unions are used to bind similar types of data
structures. In this unit, you will study about a Preprocessor. Preprocessor is a
unique feature of C language. The C preprocessor provides several tools that
are unavailable in other high-level languages. Conceptually, the
“preprocessor'' is a translation phase that is applied to your source code before
the compiler proper gets its hands on it. (Once upon a time, the preprocessor
was a separate program, much as the compiler and linker may still be separate
programs today.) Generally, the preprocessor performs textual substitutions on
your source code, in three sorts of ways:
• File inclusion: inserting the contents of another file into your source file,
as if you had typed it all in there.
• Macro substitution: replacing instances of one piece of text with
another.
• Conditional compilation: Arranging that, depending on various
circumstances, certain parts of your source code are seen or not seen by
the compiler at all.
The next three sections will introduce these three preprocessing functions.
The syntax of the preprocessor is different from the syntax of the rest of C in
several respects. First of all, the preprocessor is “line based.'' Each of the
preprocessor directives we're going to learn about (all of which begin with the
# character) must begin at the beginning of a line, and each ends at the end of
the line. (The rest of C treats line ends as just another whitespace character,
and doesn't care how your program text is arranged into lines.) Secondly, the
preprocessor does not know about the structure of C -- about functions,
what the Standard header files such as stdio.h are collections of declarations
(including external function prototype declarations) having to do with various
sets of Standard library functions. When you use #include to read in a header
file, you automatically get the prototypes and other declarations it contains, and
you should use header files, precisely so that you will get the prototypes and
other declarations they contain.
The difference between the <> and "" forms is where the preprocessor
searches for filename.h. As a general rule, it searches for files enclosed in <>
in central, standard directories, and it searches for files enclosed in "" in the
“current directory,'' or the directory containing the source file that's doing the
including. Therefore, "" is usually used for header files you've written, and <>
is usually used for headers which are provided for you (which someone else
has written).
The extension “.h'', by the way, simply stands for “header,'' and reflects the fact
that #include directives usually sit at the top (head) of your source files, and
contain global declarations and definitions which you would otherwise put
there. (That extension is not mandatory, you can theoretically name your own
header files anything you wish, but .h is traditional, and recommended.)
As we've already begun to see, the reason for putting something in a header
file, and then using #include to pull that header file into several different source
files, is when the something (whatever it is) must be declared or defined
consistently in all of the source files. If, instead of using a header file, you typed
the something in to each of the source files directly, and the something ever
changed, you'd have to edit all those source files, and if you missed one, your
program could fail in subtle (or serious) ways due to the mismatched
declarations (i.e. due to the incompatibility between the new declaration in one
source file and the old one in a source file you forgot to
change). Placing common declarations and definitions into header files means
that if they ever change, they only have to be changed in one place, which is a
much more workable system.
What should you put in header files?
• External declarations of global variables and functions. We said that a
global variable must have exactly one defining instance, but that it can
have external declarations in many places. We said that it was a grave
error to issue an external declaration in one place saying that a variable or
Manipal University Jaipur Page No.: 181
Programming in C Unit 1
function has one type, when the defining instance in some other place
actually defines it with another type. (If the two places are two source files,
separately compiled, the compiler will probably not even catch the
discrepancy.) If you put the external declarations in a header file, however,
and include the header wherever it's needed, the declarations are virtually
guaranteed to be consistent. It's a good idea to include the header in the
source file where the defining instance appears, too, so that the compiler
can check that the declaration and definition match. (That is, if you ever
change the type, you do still have to change it in two places: in the source
file where the defining instance occurs, and in the header file where the
external declaration appears. But at least you don't have to change it in an
arbitrary number of places, and, if you've set things up correctly, the
compiler can catch any remaining mistakes.)
• Preprocessor macro definitions (which we'll meet in the next section).
• Structure definitions
• Typedef declarations
themselves. The actual functions will be combined into your program at the
end of compilation, by the part of the compiler called the linker. The linker may
have to get the functions out of libraries, or you may have to tell the
compiler/linker where to find them. In particular, if you are trying to use a third-
party library containing some useful functions, the library will often come with
a header file describing those functions. Using the library is therefore a two-
step process: you must #include the header in the files where you call the
library functions, and you must tell the linker to read in the functions from the
library itself.
Self Assessment Questions:
1. # include is called _______ .
2. Nesting of included files is not allowed. (True/False)
3. Defining instances of global variables is not recommended in the header
files. (True/False)
macro:
#define MAXLINE 100
char line[MAXLINE];
...
getline(line, MAXLINE);
Now, if we ever want to change the size, we only have to do it in one place,
and it's more obvious what the words MAXLINE sprinkled through the program
mean than the magic numbers 100 did.
Since the replacement text of a preprocessor macro can be anything, it can
also be an expression, although you have to realize that, as always, the text is
substituted (and perhaps evaluated) later. No evaluation is performed when
the macro is defined. For example, suppose that you write something like
#define A 2
#define B 3
#define C A + B
(this is a pretty meaningless example, but the situation does come up in
practice). Then, later, suppose that you write
int x = C * 2;
If A, B, and C were ordinary variables, you'd expect x to end up with the value
10. But let's see what happens.
The preprocessor always substitutes text for macros exactly as you have
written it. So it first substitutes the replacement text for the macro C, resulting
in
int x = A + B * 2;
Then it substitutes the macros A and B, resulting in
int x = 2 + 3 * 2;
Only when the preprocessor is done doing all this substituting does the
compiler get into the act. But when it evaluates that expression (using the
normal precedence of multiplication over addition), it ends up initializing x with
the value 8!
To guard against this sort of problem, it is always a good idea to include explicit
parentheses in the definitions of macros which contain expressions. If we were
to define the macro C as
#define C (A + B)
then the declaration of x would ultimately expand to
int x = (2 + 3) * 2;
and x would be initialized to 10, as we probably expected.
Notice that there does not have to be (and in fact there usually is not) a
semicolon at the end of a #define line. (This is just one of the ways that the
syntax of the preprocessor is different from the rest of C.) If you accidentally
type
#define MAXLINE 100; /* WRONG */
then when you later declare
char line[MAXLINE];
the preprocessor will expand it to
char line[100;]; /* WRONG */
which is a syntax error. This is what we mean when we say that the
preprocessor doesn't know much of anything about the syntax of C-- in this last
example, the value or replacement text for the macro MAXLINE was the 4
characters 1 0 0 ; , and that's exactly what the preprocessor substituted (even
though it didn't make any sense).
Simple macros like MAXLINE act sort of like little variables, whose values are
constant (or constant expressions). It's also possible to have macros which
look like little functions (that is, you invoke them with what looks like function
call syntax, and they expand to replacement text which is a function of the
actual arguments they are invoked with) but we won't be looking at these yet.
11.3.1 Macros with arguments
The preprocessor permits us to define more complex and more useful form of
replacements. It takes the form:
# define identifiedf1, f2, ..., fn) string
Notice that there is no space between the macro identifier and the left
parenthesis. The identifiers f1, f2, ., fn are the formal macro arguments that are
analogous to the formal arguments in a function definition.
There is a basic difference between the simple replacement discussed above
Manipal University Jaipur Page No.: 185
Programming in C Unit 1
11.5 Summary
Preprocessor is a unique feature of C language. The C preprocessor provides
several tools that are unavailable in other high-level languages. The
preprocessor is a program that processes the source code before it passes
through the compiler. Macro substitution is a process where an identifier in a
11.9 Exercises
1. Write a macro definition to find the largest of three numbers.
2. Write a macro definition to compare two numbers.
3. What are the advantages of using macro definitions in a program?
4. Define a macro, PRINT_VALUE which can be used to print two values of
arbitrary type.
5. Identify errors, if any, in the following macro definition.
#define ABS(x) (x>0)?(x):(-x)
Unit 12 Dynamic Memory Allocation and
Linked List
Structure:
12.1 Introduction
Objectives
12.2 Dynamic Memory Allocation
Allocating Memory with malloc
Allocating Memory with calloc
Freeing Memory
Reallocating Memory Blocks
12.3 Pointer Safety
12.4 The Concept of linked list
Inserting a node by using Recursive Programs
Sorting and Reversing a Linked List
Deleting the Specified Node in a Singly Linked List
12.5 Summary
12.6 Terminal Questions
12.7 Answers to Self Assessment Questions
12.8 Answers for TerminalQuestions
12.9 Exercises
12.1 Introduction
In the previous unit, you studied about the preprocessor in C. You studied that
the preprocessor is a program that processes the source code before it passes
through the compiler and hence it can be used to make C an easy and efficient
language. In this unit, you will study about the dynamic memory allocation
techniques. You will also study about the linked list that is another useful
application using C.
You can use an array when you want to process data of the same data type
and you know the size of the data. Sometimes you may want to process the
data but you don't know what the size of the data is. An example of this is when
you are reading from a file or keyboard and you want to process the values. In
such a case, an array is not useful because you don't know what the dimension
of the array should be. C has the facility of dynamic memory allocation. Using
this, you can allocate the memory for your storage. The allocation is done at
runtime. When your work is over, you can deallocate the memory. The
allocation of memory is done using three functions: malloc, calloc, and realloc.
These functions return the pointers to void, so it can be typecast to any data
type, thus making the functions generic. These functions take the input as the
size of memory requirement.
Objectives:
After studying this unit, you should be able to:
• explain about the dynamic memory allocation
• implement dynamic memory allocation functions like -malloc, calloc and
realloc
• explain the concept of linked lists
• discuss the different operations on linked lists
char *line;
int linelen = 100;
line = (char *)malloc(linelen);
/* incomplete -- malloc's return value not checked */ getline(line,
linelen);
malloc is declared in <stdlib.h>, so we #include that header in any program
that calls malloc. A “byte'' in C is, by definition, an amount of storage suitable
for storing one character, so the above invocation of malloc gives us exactly
as many chars as we ask for. We could illustrate the resulting pointer like this:
The 100 bytes of memory (not all of which are shown) pointed to by line are
those allocated by malloc. (They are brand-new memory, conceptually a bit
different from the memory which the compiler arranges to have allocated
automatically for our conventional variables. The 100 boxes in the figure don't
have a name next to them, because they're not storage for a variable we've
declared.)
As a second example, we might have occasion to allocate a piece of memory,
and to copy a string into it with strcpy:
char *p = (char *)malloc(15);
/* incomplete -- malloc's return value not checked */
strcpy(p, "Hello, world!");
When copying strings, remember that all strings have a terminating \0
character. If you use strlen to count the characters in a string for you, that count
will not include the trailing \0, so you must add one before calling malloc:
char *somestring, *copy;
...
copy = (char *)malloc(strlen(somestring) + 1); /* +1 for \0 */
/* incomplete -- malloc's return value not checked */ strcpy(copy,
somestring);
What if we're not allocating characters, but integers? If we want to allocate 100
ints, how many bytes is that? If we know how big ints are on our machine (i.e.
depending on whether we're using a 16- or 32-bit machine) we could try to
compute it ourselves, but it's much safer and more portable to let C compute it
for us. C has a sizeof operator, which computes the size, in bytes, of a variable
or type. It's just what we need when calling malloc. To allocate space for 100
ints, we could call
int *ip =(int *)malloc(100 * sizeof(int));
The use of the sizeof operator tends to look like a function call, but it's really
an operator, and it does its work at compile time.
Since we can use array indexing syntax on pointers, we can treat a pointer
variable after a call to malloc almost exactly as if it were an array. In particular,
after the above call to malloc initializes ip to point at storage for 100 ints, we
can access ip[0], ip[1], ... up to ip[99]. This way, we can get the effect of an
array even if we don't know until run time how big the “array'' should be. (In a
later section we'll see how we might deal with the case where we're not even
sure at the point we begin using it how big an “array'' will eventually have to
be.)
Our examples so far have all had a significant omission: they have not checked
malloc's return value. Obviously, no real computer has an infinite amount of
memory available, so there is no guarantee that malloc will be able to give us
as much memory as we ask for. If we call malloc(100000000), or if we call
malloc(10) 10,000,000 times, we're probably going to run out of memory.
When malloc is unable to allocate the requested memory, it returns a null
pointer. A null pointer, remember, points definitively nowhere. It's a “not a
pointer'' marker; it's not a pointer you can use. Therefore, whenever you call
malloc, it's vital to check the returned pointer before using it! If you call malloc,
and it returns a null pointer, and you go off and use that null pointer as if it
pointed somewhere, your program probably won't last long. Instead, a program
should immediately check for a null pointer, and if it receives one, it should at
the very least print an error message and exit, or perhaps figure out some way
of proceeding without the memory it asked for. But it cannot go on to use the
null pointer it got back from malloc in any way, because that null pointer by
definition points nowhere. (“It cannot use a null pointer in any way'' means that
the program cannot use the * or [] operators on such a pointer value, or pass
it to any function that expects a valid pointer.)
Manipal University Jaipur Page No.: 195
Programming in C Unit 1
A call to malloc, with an error check, typically looks something like this:
int *ip = (int *)malloc(100 * sizeof(int));
if(ip == NULL)
{
printf("out of memory\n"); exit or return
}
After printing the error message, this code should return to its caller, or exit
from the program entirely; it cannot proceed with the code that would have
used ip.
Of course, in our examples so far, we've still limited ourselves to “fixed size''
regions of memory, because we've been calling malloc with fixed arguments
like 10 or 100. (Our call to getline is still limited to 100-character lines, or
whatever number we set the linelen variable to; our ip variable still points at
only 100 ints.) However, since the sizes are now values which can in principle
be determined at run-time, we've at least moved beyond having to recompile
the program (with a bigger array) to accommodate longer lines, and with a little
more work, we could arrange that the “arrays'' automatically grew to be as large
as required. (For example, we could write something like getline which could
read the longest input line actually seen).
12.2.2 Allocating memory with calloc
calloc is another memory allocation function that is normally used for
requesting memory space at run time for storing derived data types such as
arrays and structures. While malloc allocates a single block of storage space,
calloc allocates multiple blocks of storage, each of the same size, and then
sets all bytes to zero. The general form of calloc is:
ptr=(cast-type *)calloc(n, elem-size);
The above statement allocates contiguous space for n blocks, each of size
elem-size bytes. All bytes are initialized to zero and a pointer to the first byte
of the allocated region is returned. If there is not enough space, a NULL pointer
is returned.
Program 12.1 Program to illustrate the use of malloc and calloc
functions
#include <stdio.h>
#include <malloc.h>
Manipal University Jaipur Page No.: 196
Programming in C Unit 1
main()
{
int *base;
int i;
int cnt=0;
int sum=0;
printf("how many integers you have to store \n");
scanf("%d",&cnt);
base = (int *)malloc(cnt * sizeof(int));
printf("the base of allocation is %16lu \n",base);
if(!base)
printf("unable to allocate size \n");
else
{
for(int j=0;j<cnt;j++)
*(base+j)=5;
}
sum = 0;
for(int j=0;j<cnt;j++)
sum = sum + *(base+j);
printf("total sum is %d\n",sum);
free(base);
printf("the base of allocation is %16lu \n",base);
base = (int *)malloc(cnt * sizeof(int));
printf("the base of allocation is %16lu \n",base);
base = (int *)malloc(cnt * sizeof(int));
printf("the base of allocation is %16lu \n",base);
base = (int *)calloc(10,2);
printf("the base of allocation is %16lu \n",base);
}
12.2.3 Freeing Memory
Memory allocated with malloc lasts as long as you want it to. It does not
automatically disappear when a function returns, as automatic variables do,
but it does not have to remain for the entire duration of your program, either.
Just as you can use malloc to control exactly when and how much memory
you allocate, you can also control exactly when you deallocate it.
In fact, many programs use memory on a transient basis. They allocate some
Manipal University Jaipur Page No.: 197
Programming in C Unit 1
memory, use it for a while, but then reach a point where they don't need that
particular piece any more. Because memory is not inexhaustible, it's a good
idea to deallocate (that is, release or free) memory you're no longer using.
Dynamically allocated memory is deallocated with the free function. If p
contains a pointer previously returned by malloc, you can call
free(p);
which will “give the memory back'' to the stock of memory (sometimes called
the “arena'' or “pool'') from which malloc requests are satisfied. Calling free is
sort of the ultimate in recycling: it costs you almost nothing, and the memory
you give back is immediately usable by other parts of your program.
(Theoretically, it may even be usable by other programs.)
(Freeing unused memory is a good idea, but it's not mandatory. When your
program exits, any memory which it has allocated but not freed should be
automatically released. If your computer were to somehow “lose'' memory just
because your program forgot to free it, that would indicate a problem or
deficiency in your operating system.)
Naturally, once you've freed some memory you must remember not to use it
any more. After calling
free(p);
it is probably the case that p still points at the same memory. However, since
we've given it back, it's now “available,'' and a later call to malloc might give
that memory to some other part of your program. If the variable p is a global
variable or will otherwise stick around for a while, one good way to record the
fact that it's not to be used any more would be to set it to a null pointer:
free(p);
p = NULL;
Now we don't even have the pointer to the freed memory any more, and (as
long as we check to see that p is non-NULL before using it), we won't misuse
any memory via the pointer p.
When thinking about malloc, free, and dynamically-allocated memory in
general, remember again the distinction between a pointer and what it points
to. If you call malloc to allocate some memory, and store the pointer which
malloc gives you in a local pointer variable, what happens when the function
containing the local pointer variable returns? If the local pointer variable has
Manipal University Jaipur Page No.: 198
Programming in C Unit 1
automatic duration (which is the default, unless the variable is declared static),
it will disappear when the function returns. But for the pointer variable to
disappear says nothing about the memory pointed to! That memory still exists
and, as far as malloc and free are concerned, is still allocated. The only thing
that has disappeared is the pointer variable you had which pointed at the
allocated memory. (Furthermore, if it contained the only copy of the pointer you
had, once it disappears, you'll have no way of freeing the memory, and no way
of using it, either. Using memory and freeing memory both require that you
have at least one pointer to the memory!)
12.2.4 Reallocating Memory Blocks
Sometimes you're not sure at first how much memory you'll need. For example,
if you need to store a series of items you read from the user, and if the only
way to know how many there are is to read them until the user types some
“end'' signal, you'll have no way of knowing, as you begin reading and storing
the first few, how many you'll have seen by the time you do see that “end''
marker. You might want to allocate room for, say, 100 items, and if the user
enters a 101st item before entering the “end'' marker, you might wish for a way
to say ”uh, malloc, remember those 100 items I asked for? Could I change my
mind and have 200 instead?''
In fact, you can do exactly this, with the realloc function. You hand realloc an
old pointer (such as you received from an initial call to malloc) and a new size,
and realloc does what it can to give you a chunk of memory big enough to hold
the new size. For example, if we wanted the ip variable from an earlier example
to point at 200 ints instead of 100, we could try calling
ip = realloc(ip, 200 * sizeof(int));
Since you always want each block of dynamically-allocated memory to be
contiguous (so that you can treat it as if it were an array), you and realloc have
to worry about the case where realloc can't make the old block of memory
bigger “in place,'' but rather has to relocate it elsewhere in order to find enough
contiguous space for the new requested size. realloc does this by returning a
new pointer. If realloc was able to make the old block of memory bigger, it
returns the same pointer. If realloc has to go elsewhere to get enough
contiguous memory, it returns a pointer to the new memory, after copying your
old data there. (In this case, after it makes the copy, it frees the old block.)
Finally, if realloc can't find enough memory to satisfy the new request at all, it
returns a null pointer. Therefore, you usually don't want to overwrite your old
pointer with realloc's return value until you've tested it to make sure it's not a
null pointer. You might use code like this:
int *newp;
newp = realloc(ip, 200 * sizeof(int));
if(newp != NULL)
ip = newp;
else {
printf("out of memory\n");
/* exit or return */
/* but ip still points at 100 ints */ }
If realloc returns something other than a null pointer, it succeeded, and we set
ip to what it returned. (We've either set ip to what it used to be or to a new
pointer, but in either case, it points to where our data is now.) If realloc returns
a null pointer, however, we hang on to our old pointer in ip which still points at
our original 100 values.
Putting this all together, here is a piece of code which reads lines of text from
the user, treats each line as an integer by calling atoi, and stores each integer
in a dynamically-allocated “array'':
#define MAXLINE 100
char line[MAXLINE];
int *ip;
int nalloc, nitems;
nalloc = 100;
ip = (int *)malloc(nalloc * sizeof(int)); /* initial allocation */
if(ip == NULL)
{
printf("out of memory\n");
exit(1);
}
nitems = 0;
ip[nitems++] = atoi(line);
}
We use two different variables to keep track of the “array'' pointed to by ip.
nalloc is how many elements we've allocated, and nitems is how many of them
are in use. Whenever we're about to store another item in the “array,'' if nitems
>= nalloc, the old “array'' is full, and it's time to call realloc to make it bigger.
Finally, we might ask what the return type of malloc and realloc is, if they are
able to return pointers to char or pointers to int or (though we haven't seen it
yet) pointers to any other type. The answer is that both of these functions are
declared (in <stdlib.h>) as returning a type we haven't seen, void * (that
15, pointer to void). We haven't really seen type void, either, but what's going
on here is that void * is specially defined as a “generic'' pointer type, which may
be used (strictly speaking, assigned to or from) any pointer type.
Therefore, a linked list is a list of elements in which the elements of the list can
be placed anywhere in memory, and these elements are linked with each other
using an explicit link field, that is, by storing the address of the next element in
the link field of the previous element.
Program 12.2: Here is a program for building and printing the elements
of the linked list
# include <stdio.h>
# include <stdlib.h> struct node
{
int data;
struct node *link;
};
struct node *insert(struct node *p, int n)
{
struct node *temp;
/* if the existing list is empty then insert a new node as the
starting node */
if(p==NULL)
{
p=(struct node *)malloc(sizeof(struct node)); /* creates new node data
value passes as parameter */
if(p==NULL)
{
printf("Error\n");
exit(0);
}
p-> data = n;
p-> link = p; /* makes the pointer pointing to itself because it
is a circular list*/
}
else
{
temp = p;
/* traverses the existing list to get the pointer to the last node of
it */
while (temp-> link != p)
temp = temp-> link;
void main()
{
int n;
int x;
struct node *start = NULL ;
printf("Enter the nodes to be created \n");
scanf("%d",&n);
while ( n -- > 0 )
Manipal University Jaipur Page No.: 205
Programming in C Unit 1
{
printf( "Enter the data values to be placed in a node\n");
scanf("%d",&x);
start = insert ( start, x );
}
printf("The created list is\n");
printlist ( start );
}
12.4.1 Inserting a node by using Recursive Programs
A linked list is a recursive data structure. A recursive data structure is a data
structure that has the same form regardless of the size of the data. You can
easily write recursive programs for such data structures.
Program 12.3
# include <stdio.h>
# include <stdlib.h>
struct node
{
int data;
struct node *link;
};
struct node *insert(struct node *p, int n)
{
struct node *temp;
if(p==NULL)
{
p=(struct node *)malloc(sizeof(struct node));
if(p==NULL)
{
printf("Error\n");
exit(0);
}
p-> data = n;
p-> link = NULL;
}
else
p->link = insert(p->link,n);/* the while loop replaced by recursive call */
return (p);
}
Manipal University Jaipur Page No.: 206
Programming in C Unit 1
{
Next = curr->link;
Curr -> link = prev;
Prev = curr;
Curr = next;
}
Program 12.4: Example in sorting lists.
# include <stdio.h>
# include <stdlib.h> struct node {
int data;
struct node *link;
};
struct node *insert(struct node *p, int n)
{
struct node *temp;
if(p==NULL)
{
p=(struct node *)malloc(sizeof(struct node));
if(p==NULL)
{
printf("Error\n");
exit(0);
}
p-> data = n;
p-> link = NULL;
}
else
{
temp = p;
while (temp-> link!= NULL)
temp = temp-> link;
temp-> link = (struct node *)malloc(sizeof(struct node));
if(temp -> link == NULL)
{
printf("Error\n");
exit(0);
}
temp = temp-> link;
Manipal University Jaipur Page No.: 208
Programming in C Unit 1
temp-> data = n;
temp-> link = null;
}
return(p);
}
void main()
{
int n;
int x;
struct node *start = NULL ;
Manipal University Jaipur Page No.: 210
Programming in C Unit 1
scanf("%d",&x);
start = insert ( start,x);
}
printf("The created list is\n");
printlist ( start );
start = sortlist(start);
printf("The sorted list is\n");
printlist ( start );
start = reverse(start);
printf("The reversed list is\n"); printlist ( start );
}
12.4.3 Deleting the specified node in a singly linked list
To delete a node, first we determine the node number to be deleted (this is
based on the assumption that the nodes of the list are numbered serially from
1 to n). The list is then traversed to get a pointer to the node whose number is
given, as well as a pointer to a node that appears before the node to be deleted.
Then the link field of the node that appears before the node to be deleted is
made to point to the node that appears after the node to be deleted, and the
node to be deleted is freed.
Program 12.5: Example of working with nodes.
# include <stdio.h>
# include <stdlib.h>
struct node *delet ( struct node *, int );
int length ( struct node * );
struct node
{
int data;
struct node *link;
};
struct node *insert(struct node *p, int n)
Manipal University Jaipur Page No.: 211
Programming in C Unit 1
{
struct node *temp;
if(p==NULL)
{
p=(struct node *)malloc(sizeof(struct node));
if(p==NULL)
{
printf("Error\n");
exit(0);
}
p-> data = n;
p-> link = NULL;
}
else
{
temp = p;
while (temp-> link != NULL)
temp = temp-> link;
temp-> link = (struct node *)malloc(sizeof(struct node));
if(temp -> link == NULL)
{
printf("Error\n");
exit(0);
}
temp = temp-> link;
temp-> data = n;
temp-> link = NULL;
}
return (p);
}
void printlist ( struct node *p )
{
printf("The data values in the list are\n");
while (p!= NULL)
{
printf("%d\t",p-> data);
p = p-> link;
}
Manipal University Jaipur Page No.: 212
Programming in C Unit 1
}
void main()
{
int n;
int x;
struct node *start = NULL;
printf("Enter the nodes to be created \n");
scanf("%d",&n);
while ( n-- > 0 )
{
printf( "Enter the data values to be placed in a node\n"); scanf("%d",&x);
start = insert ( start, x );
}
printf(" The list before deletion id\n");
printlist ( start );
printf("% \n Enter the node no \n");
scanf ( " %d",&n);
start = delet (start , n );
printf(" The list after deletion is\n");
printlist ( start );
}
/* a function to delete the specified node*/ struct node *delet ( struct node
*p, int node_no )
{
struct node *prev, *curr ;
int i;
if (p == NULL )
{
printf("There is no node to be deleted \n");
}
else
{
if ( node_no > length (p))
{
printf("Error\n");
}
Manipal University Jaipur Page No.: 213
Programming in C Unit 1
else
{
prev = NULL;
curr = p;
i=1;
while ( i < node_no )
{
prev = curr;
curr = curr-> link;
i = i+1;
}
if ( prev == NULL )
{
p = curr -> link; free ( curr );
} else {
prev -> link = curr -> link ; free ( curr );
}
}
} return(p);
}
/* a function to compute the length of a linked list */ int length ( struct node *p
)
{
int count = 0 ;
while ( p != NULL )
{
count++;
p = p->link;
}
return ( count ) ;
}
Self Assessment Questions
5. A linked list is a data structure that is used to model a dynamic list of data
items and is a collection of ______________ .
6. Linked list make use of ___________ memory allocation technique.
12.5 Summary
Manipal University Jaipur Page No.: 214
Programming in C Unit 1
3. ptr=ptr->link;
4. A linked list is a data structure that is used to model a dynamic list of data
items and is a collection of nodes. (Refer to section 12.4 for more details)
12.9 Exercises
1. Write a menu driven program to create a linked list of a class of students
and perform the following operations.
i. Write out the contents of a list
ii. Edit the details of a specified student
iii. Count the number of students above a specified age and weight
2. Write a program to insert a node after the specified node in alinkedlist.
3. Write a program to count the number of nodesin linked list.
4. Write a program to merge two sorted lists.
5. Explainbriefly how to represent polynomials using linked lists.Write a
program to add two polynomials.
Unit 13 File Management
Structure:
13.1 Introduction
Objectives
13.2 Defining and Openinga file
13.3 Closing Files
13.4 Input/Output Operationson Files
Predefined Streams
13.5 Error Handling during I/O Operations
13.6 Random Access to Files
13.7 Command Line Arguments
13.8 Summary
13.9 Terminal Questions
13.10 Answers to Self Assessment Questions
13.11 Answers to Terminal Questions
13.12 Exercises
13.1 Introduction
In nearly all the previous units, so far, we've been calling printf to print
formatted output to the “standard output'' (wherever that is). We've also been
Manipal University Jaipur Page No.: 216
Programming in C Unit 1
calling getchar to read single characters from the “standard input,'' and
putchar to write single characters to the standard output. “Standard input'' and
“standard output'' are two predefined I/O streams which are implicitly available
to us. In this unit, you will study how to take control of input and output by
opening our own streams, perhaps connected to data files, which we can read
from and write to.
Objectives:
After studying this unit, you should be able to:
• open your file and control input and output
• connect to file and able to read from the file and write to the file using file
pointers.
• checkg for error during I/O operations on files.
• implement the Random Access functions to allow control over the implied
read/write position in the file.
• supply a parameter to a program when the program is invoked using a
command line arguments.
13.2 Defining and Opening a File
How will we specify that we want to access a particular data file? It would
theoretically be possible to mention the name of a file each time it was desired
to read from or write to it. But such an approach would have a number of
drawbacks. Instead, the usual approach (and the one taken in C's stdio library)
is that you mention the name of the file once, at the time you open it.
Thereafter, you use some little token in this case, the file pointer which keeps
track (both for your sake and the library's) of which file you're talking about.
Whenever you want to read from or write to one of the files you're working with,
you identify that file by using its file pointer (that is, the file pointer you obtained
when you opened the file). As we'll see, you store file pointers in variables just
as you store any other data you manipulate, so it is possible to have several
files open, as long as you use distinct variables to store the file pointers.
You declare a variable to store a file pointer like this:
FILE *fp;
The type FILE is predefined for you by <stdio.h>. It is a data structure which
holds the information the standard I/O library needs to keep track of the file for
you. For historical reasons, you declare a variable which is a pointer to this
FILE type. The name of the variable can (as for any variable) be anything you
choose; it is traditional to use the letters fp in the variable name (since we're
talking about a file pointer). If you were reading from two files at once you'd
probably use two file pointers:
FILE *fp1, *fp2;
If you were reading from one file and writing to another you might declare an
input file pointer and an output file pointer:
FILE *ifp, *ofp;
Like any pointer variable, a file pointer isn't good until it's initialized to point to
something. (Actually, no variable of any type is much good until you've
initialized it.) To actually open a file, and receive the “token'' which you'll store
in your file pointer variable, you call fopen. fopen accepts a file name (as a
string) and a mode value indicating among other things whether you intend to
read or write this file. (The mode variable is also a string.) To open the file
input.dat for reading you might call
ifp = fopen("input.dat", "r");
The mode string "r" indicates reading. Mode "w" indicates writing, so we could
open output.dat for output like this:
ofp = fopen("output.dat", "w");
The other values for the mode string are less frequently used. The third major
mode is "a" for append. (If you use "w" to write to a file which already exists,
its old contents will be discarded.) You may also add “+’’ character to the mode
string to indicate that you want to both read and write, or a “b’’ character to
indicate that you want to do “binary'' (as opposed to text) I/O.
One thing to beware of when opening files is that it's an operation which may
fail. The requested file might not exist, or it might be protected against reading
or writing. (These possibilities ought to be obvious, but it's easy to forget them.)
fopen returns a null pointer if it can't open the requested file, and it's important
to check for this case before going off and using fopen's return value as a file
pointer. Every call to fopen will typically be followed with a test, like this:
ifp = fopen("input.dat", "r");
if(ifp == NULL)
{
printf("can't open file\n"); exit or return
Manipal University Jaipur Page No.: 218
Programming in C Unit 1
}
If fopen returns a null pointer, and you store it in your file pointer variable and
go off and try to do I/O with it, your program will typically crash.
It's common to collapse the call to fopen and the assignment in with the test:
if((ifp = fopen("input.dat", "r")) == NULL)
{
printf("can't open file\n");
exit or return
}
You don't have to write these “collapsed'' tests if you're not comfortable with
them, but you'll see them in other people's code, so you should be able to read
them.
Self Assessment Questions
1. The type FILE is predefined in the header file __________ .
2. We may add a “+’’ character to the mode string in the fopen function to
indicate that we want to both read and write. (True/False)
exits. (True/False)
}
if(c == EOF && nch == 0) return EOF;
line[nch] = '\0';
return nch;
}
Now we could read one line from ifp by calling char line[MAXLINE]; ...
fgetline(ifp, line, MAXLINE);
“standard error output''; error messages printed to it will not disappear into an
output file. For example, a more realistic way to print an error message when
a file can't be opened would be
if((ifp = fopen(filename, "r")) == NULL)
{
fprintf(stderr, "can't open file %s\n", filename); exit or return
}
where filename is a string variable indicating the file name to be opened. Not
only is the error message printed to stderr, but it is also more informative in
that it mentions the name of the file that couldn't be opened.
Program 13.2: To read a data file input.dat
Suppose you had a data file consisting of rows and columns of numbers:
1 2 34
5 6 78
9 10 112
Suppose you wanted to read these numbers into an array. (Actually, the array
will be an array of arrays, or a “multidimensional'' array; ) We can write code to
do this by putting together several pieces: the fgetline function we just showed,
and the getwords function from which each line broken into words. Assuming
that the data file is named input.dat, the code would look like this:
#define MAXLINE 100
#define MAXROWS 10
#define MAXCOLS 10
int array[MAXROWS][MAXCOLS];
char *filename = "input.dat";
FILE *ifp;
char line[MAXLINE];
char *words[MAXCOLS];
int nrows = 0;
int n;
int i;
{
fprintf(stderr, "can't open %s\n", filename);
exit(EXIT_FAILURE);
} while(fgetline(ifp, line, MAXLINE) != EOF) { if(nrows >=
MAXROWS) { fprintf(stderr, "too many rows\n"); exit(EXIT_FAILURE);
}
fclose(stdout);
if(fgetc(stdout) >= 0){
fprintf(stderr, "What - no error!\n"); exit(EXIT_FAILURE);
}
perror("fgetc");
exit(EXIT_SUCCESS);
}
/* Result */
fgetc: Bad file number
immediately following the end of the data just written. (Remember that stdio
insists on the user inserting a buffer-flushing operation between each element
of a read-write-read cycle.) To control this, the Random Access functions allow
control over the implied read/write position in the file. The file position indicator
is moved without the need for a read or a write, and indicates the byte to be
the subject of the next operation on the file.
Three types of function exist which allow the file position indicator to be
examined or changed. Their declarations and descriptions follow.
#include <stdio.h> /* return file position indicator */ long ftell(FILE *stream);
int fgetpos(FILE *stream, fpos_t *pos);
/* set file position indicator */ int fseek(FILE *stream, long offset, int ptrname);
int fsetpos(FILE *stream, const fpos_t *pos);
ftell returns the current value (measured in characters) of the file position
indicator if stream refers to a binary file. For a text file, a ‘magic’ number is
returned, which may only be used on a subsequent call to fseek to reposition
to the current file position indicator. On failure, -1L is returned and errno is set.
rewind sets the current file position indicator to the start of the file indicated by
stream. The file's error indicator is reset by a call of rewind. No value is
returned.
fseek allows the file position indicator for stream to be set to an arbitrary value
(for binary files), or for text files, only to a position obtained from ftell, as follows:
• In the general case, the file position indicator is set to offset bytes
(characters) from a point in the file determined by the value of ptrname.
Offset may be negative. The values of ptrname may be SEEK_SET, which
sets the file position indicator relative to the beginning of the file,
SEEK_CUR, which sets the file position indicator relative to its current
value, and SEEK_END, which sets the file position indicator relative to the
end of the file. The latter is not necessarily guaranteed to work properly on
binary streams.
• For text files, offset must either be zero or a value returned from a previous
call to ftell for the same stream, and the value of ptrname must be
SEEK_SET.
• fseek clears the end of file indicator for the given stream and erases the
memory of any ungetc. It works for both input and output.
• Zero is returned for success, non-zero for a forbidden request.
Note that for ftell and fseek it must be possible to encode the value of the file
position indicator into a long. This may not work for very long files, so the
Standard introduces fgetpos and fsetpos which have been specified in a way
that removes the problem.
fgetpos stores the current file position indicator for stream in the object pointed
to by pos. The value stored is ‘magic’ and only used to return to the specified
position for the same stream using fsetpos.
fsetpos works as described above, also clearing the stream's end-of-file
indicator and forgetting the effects of any ungetc operations.
For both functions, on success, zero is returned; on failure, non-zero is
returned and errno is set.
Self Assessment Questions
10. function returns the current value (measured in
characters) of the file position indicator if stream refers to a binary file.
11. fseek allows the file position indicator for stream to be set to an arbitrary
value. (True/False)
12. stores the current file position indicator for stream in the
object pointed to by pos.
command line includes the command you typed to invoke your program (that
is, the name of your program). In other words, argv[0] typically points to the
name of your program, and argv[1] is the first argument.
There are no hard-and-fast rules for how a program should interpret its
command line. There is one set of conventions for Unix, another for MS- DOS,
another for VMS. Typically you'll loop over the arguments, perhaps treating
some as option flags and others as actual arguments (input files, etc.),
interpreting or acting on each one. Since each argument is a string, you'll have
to use strcmp or the like to match arguments against any patterns you might
be looking for. Remember that argc contains the number of words on the
command line, and that argv[0] is the command name, so if argc is 1, there are
no arguments to inspect. (You'll never want to look at argv[i], for i >= argc,
because it will be a null or invalid pointer.)
A further example of the use of argc and argv now follows:
fclose(fp); }
return 0;
}
As a historical note, the Unix cat program is so named because it can be used
to concatenate two files together, like this:
cat a b > c
This illustrates why it's a good idea to print error messages to stderr, so that
they don't get redirected. The “can't open file'' message in this example also
includes the name of the program as well as the name of the file.
Yet another piece of information which it's usually appropriate to include in
error messages is the reason why the operation failed, if known. For operating
system problems, such as inability to open a file, a code indicating the error is
often stored in the global variable errno. The standard library function strerror
will convert an errno value to a human-readable error message string.
} fclose(fp);
}
Self Assessment Questions
13. Command line argument is a parameter supplied to a program when the
program is invoked. (True/False)
14. In the command line arguments of main(), argv is an array-of-pointers- to-
___________ .
15. The ____________ is a parameter supplied to a program when the
program is invoked.
13.8 Summary
“Standard input'' and “standard output'' are two predefined I/O streams which
are implicitly available to us. To actually open a file, and receive the “token''
which you'll store in your file pointer variable, you call fopen. fopen accepts a
file name (as a string) and a mode value indicating among other things whether
you intend to read or write this file. The file pointer which keeps track (both for
your sake and the library's) of which file you're talking about.
For each of the I/O library functions we've there's a companion function which
accepts an additional file pointer argument telling it where to read from or write
to. The standard I/O functions maintain two indicators with each open stream
to show the end-of-file and error status of the stream. The Random Access
functions allow control over the implied read/write position in the file. The
command line argument is a parameter supplied to a program when the
program is invoked.
fclose(pt1);
fclose(pt2);
}
a) Read the string represented by name from the data file sample.old.
b) Display it on the screen
c) Enter the updated string.
d) Write the new string to the data file sample.new
2. What is a command line argument and what is its use? Explain
13.12 Exercises