C Programming AllClasses-Outline-99-197
C Programming AllClasses-Outline-99-197
}
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