C & Data Structures
C & Data Structures
C & Data Structures
Course Overview:
This course begins with introduction to C, delves deep into it and finishes up with
a roundup of basic data structures.
At the end of the course there will be a project presentation where the students
shall present their projects. These projects shall be graded based on their ingenuity,
relevance to the course and other key points. The grading will be done by judges who are
experts in the field. The students will then be awarded merit certificates and participation
certificates based on their projects and participation in the course.
1) Introduction to C programming
2) Variables, Constants, Printf and Scanf along with a few operators.
3) Operators in C and flow control statements.
4) Pointers in C
5) Advanced pointers and Arrays
6) Structures and Enumerations
7) Typedefs and their usage
8) Unions in C
9) An overview of graphics in C
10) Introduction to Data Structures
11) Stacks
12) Queues
13) Linked Lists
14) Binary Trees
15) Overview of advanced data structures and introduction to the next course on
algorithms.
After this there will also be a project guidance class followed by a project
presentation event, where the students shall exhibit the skills they have acquired through
their projects. These projects will be judged and they shall be awarded grades based on
their projects. The students shall be awarded with certificates upon completing the
course, based on their levels of participation in the course and their projects.
1) Introduction to C Programming
What is programming?
An Introduction to C
C language is one of the most loved languages in the history of computing. The
power and flexibility of C are amazing. It has the pride of being the language behind the
most famous UNIX and LINUX systems. C is almost always used for Systems
Programming and Operating Systems; such is the power of this magnificent language.
Data Types
The basic data types in C are
• char a character
• int an integer, in the range -32,767 to 32,767
• long int a larger integer (up to +-2,147,483,647)
• float a floating-point number
• double a floating-point number, with more precision and perhaps greater range
than float
There can be signed and unsigned modifiers to these basic types. The specified range
of values that can be taken by a basic data type must be guaranteed by any
implementation of the language, whereas the actual size of the data type may vary from
implementation to implementation.
You might wonder how the computer stores characters. The answer involves a
character set, which is simply a mapping between some set of characters and some set of
small numeric codes. Most machines today use the ASCII character set, in which the
letter A is represented by the code 65, the ampersand & is represented by the code 38, the
digit 1 is represented by the code 49, the space character is represented by the code 32,
etc. (Most of the time, of course, you have no need to know or even worry about these
particular code values; they're automatically translated into the right shapes on the screen
or printer when characters are printed out, and they're automatically generated when you
type characters on the keyboard. Eventually, though, we'll appreciate, and even take some
control over, exactly when these translations--from characters to their numeric codes--are
performed.) Character codes are usually small--the largest code value in ASCII is 126,
which is the ~ (tilde or circumflex) character. Characters usually fit in a byte, which is
usually 8 bits. In C, type char is defined as occupying one byte, so it is usually 8 bits.
The advantage of ASCII format is that the characters representing the digits ‘0’ to
‘9’occupy consecutive positions in the scheme thus facilitating easy conversions between
numeric strings and integers.
Variable Names
These names (the formal term is ``identifiers'') consist of letters, numbers, and
underscores. For our purposes, names must begin with a letter. Theoretically, names can
be as long as you want, but extremely long ones get tedious to type after a while, and the
compiler is not required to keep track of extremely long ones perfectly.
A final restriction on names is that 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).
Constants
Character Constants
Character constants are enclosed within single quotes like ‘a’, ’3’, ’)’ etc. They
can involve escape sequences, the most common of which are
\n a ``newline'' character
\b a backspace
\r a carriage return (without a line feed)
\' a single quote (e.g. in a character constant)
\" a double quote (e.g. in a string constant)
\\ a single backslash
Integer Constants
Float constants
The float constants have a decimal point in them, in which case they will
automatically considered as float. We can have an optional f suffix to explicitly use a
constant as a float constant. We could also represent floating point constants in the
exponential form like 3e-4 etc.
String Constants
These are generally called literals in some languages. They are enclosed by
double quotes ( “ ) and can have escape characters in between them.
The printf statement is the output function provided in <stdio.h> for outputting
data, this has a lot of interesting variations which we deal with later. The general format
of printf is
printf(formatstring, arguments);
The scanf statement is the input function provided as a part of the C library. This
has the general format
scanf(formatstring, arguments);
Operators in C fall into five main classes: arithmetic operators, unary operators,
relational and logical operators, assignment operators, and the conditional operator.
The basic operators for performing arithmetic are the same in many computer
languages:
+ addition
- subtraction
* multiplication
/ division
% modulus (remainder)
The - operator can be used in two ways: to subtract two numbers (as in a - b), or
to negate one number (as in -a + b or a + -b).
The modulus operator % gives you the remainder when two integers are divided: 1
% 2 is 1; 7 % 4 is 3. (The modulus operator can only be applied to integers.)
There are also prefix and postfix increment and decrement operators ++ and --
The relational and logical operators include < , > , >=, <= , == , &&, ||, ! etc
Simple conditional operations can be carried out with the conditional operator (?
:). An expression that makes use of the conditional operator is called a conditional
expression. Such an expression takes the general form
i = 0;
i = i + 1;
printf("Hello, world!\n");
c = sqrt(a*a + b*b);
The basic flow control statements are
If-else statements
if( expression )
{
statement1;
statement2;
…………………………
statementn;
}
Here is an example of another common arrangement of if and else. Suppose we
have a variable grade containing a student's numeric grade, and we want to print out the
corresponding letter grade. Here is code that would do the job:
if(grade >= 90)
printf("A");
else if(grade >= 80)
printf("B");
else if(grade >= 70)
printf("C");
else if(grade >= 60)
printf("D");
else printf("F");
Switch Statements
While Statement
while( expression )
statement
expr1
expr2
statement
expr3
expr2
statement
expr3
...
expr2
statement
expr3
expr2
Do while Loops
do {
statement1;
statement2;
…………………………
statementn;
} while (condition is satisfied)
The do loop also executes a block of code as long as a condition is satisfied. The
difference between a "do" loop and a "while" loop is that the while loop tests its
condition at the top of its loop; the "do" loop tests its condition at the bottom of its loop.
As noted above, if the test condition is false as the while loop is entered the block of code
is skipped. Since the condition is tested at the bottom of a do loop, its block of code is
always executed at least once.
Sometimes, due to an exceptional condition, you need to jump out of a loop early,
that is, before the main controlling expression of the loop causes it to terminate normally.
Other times, in an elaborate loop, you may want to jump back to the top of the loop (to
test the controlling expression again, and perhaps begin a new trip through the loop)
without playing out all the steps of the current loop. The break and continue statements
allow you to do these two things. (They are, in fact, essentially restricted forms of goto.)
Ex:- The following program prints all the prime numbers from 1 to 1000
#include <stdio.h>
#include <math.h>
int main(){
int i, j;
printf("%d\n", 2);
for(i = 3; i <= 1000; i = i + 1){
for(j = 2; j < ((i/2)+1); j = j + 1){
if(i % j == 0)
break;
if(j > sqrt(i)) {
printf("%d\n", i);
break;
}
}
}
return 0; }
4) Pointers in C
The first things to do with pointers are to declare a pointer variable, set it to point
somewhere, and finally manipulate the value that it points to. A simple pointer
declaration looks like this:
int *p;
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 p to point to it:
int i = 500;
p = &i;
The contents-of operator * does not merely fetch values through pointers; it can
also set values through pointers. We can write something like
*p = 70;
To change the contents of the pointer variable itself use
p = &j;
We're setting the initial value for p, which is where p will point, so that initial
value is a pointer. (In other words, the * in the declaration int *p = &i; is not the contents-
of operator, it's the indicator that p is a pointer.)
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;
This works for one pointer, because C essentially ignores whitespace. But if you ever
write
int* ip1, ip2; /* PROBABLY WRONG */
Pointers do not have to point to single variables. They can also point at the
contents of an array. For example, we can write
int *p;
int a[100];
p = &a[3];
Once we have a pointer pointing into an array, we can start doing pointer
arithmetic. Given that p is a pointer to a[3], we can add 1 to p:
p+1
In C, this 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:
p2 = p + 1;
If we now do
*p2 = 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:
*(p + 1) = 5;
Given that we can add 1 to a pointer, it's not surprising that we can add and
subtract other numbers as well. If p still points to a[3], then
*(p + 3) = 7;
sets a[6] to 7, and
*(p - 2) = 4;
sets a[1] to 4.
You can both compare two pointers as well as subtract two pointers, adding two
pointers is illegal because it might point to some illegal memory location.
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:
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
is illegal. As we've seen, though, you can assign two pointer variables:
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.
int a[10];
int *ip;
ip = a;
Functions in C receive copies of their arguments. (This means that C uses call
by value; it means that a function can modify one of its arguments without modifying the
value in the caller.) When a function is called, the copies of the arguments are made as if
by assignment. But since arrays can't be assigned they are passed by reference as the
address to the memory location being pointed to.
Strings are actually arrays. 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
strings are terminated by ‘\0’ character therefore we can just use that sentinel character to
detect the end of string when traversing a string by using character pointers.
#include <stdio.h>
int *ip = NULL;
We can use this NULL to check for errors when dealing with pointers to check
if they have valid values or not.
Structures
The basic user-defined data type in C is the structure, or struct. (C structures are
analogous to the records found in some other languages.)
As a simple example, suppose we wanted to define our own type for representing
complex numbers. A complex number consists of a real and imaginary part, we need a
way of holding both these quantities in one data type, and a structure will do just the
trick. Here is how we might declare our complex type:
struct complex
{
double real;
double imag;
};
We declare variables of our new complex type with declarations like these:
In which case, we would have defined the type struct complex, and right away
declared three variables c1, c2, and c3 all of type struct complex.
c1.real = 1
To set the real part of c1 (that is, the real member within c1) to 1, or
c1.imag = c2.imag
To fetch the imaginary part of c2 and assign it to the imaginary part of c1, or
c1 = c2
Pointers in C are general; we can have pointers to any type. It turns out that
pointers to structures are particularly useful.
We declare pointers to structures the same way we declare any other pointers: by
preceding the variable name with an asterisk in the declaration. We could declare two
pointers to struct complex with
And, as before, we could set these pointers to point to actual variables of type
complex:
p1 = &c2;
p2 = &c3;
Then,
*p1 = *p2;
would copy the structure pointed to by p2 to the structure pointed to by p1 (i.e. c3 to c2),
and
p1 = p2;
(*p1).real
(Without the parentheses, i.e. if we wrote simply *p1.real, we'd be taking the structure
p1, selecting its member named real, and accessing the value that p1.real points to,
which would be doubly nonfunctional, since the real member is in our ongoing example
not a pointer, and p1 is not a structure but rather a pointer to a structure, so the . operator
won't work on it.)
Since pointers to structures are common, and the parentheses in the example
above are a nuisance, there's another structure selection operator that works on pointers
to structures. If p is a pointer to a structure and m is a member of that structure, then
p->m
selects that member of the pointed-to structure. The expression p->m is therefore exactly
equivalent to
(*p).m
Enumerations
First use the keyword enum followed by the enumeration's name (or tag). The
body of the definition is enclosed in curly brackets. The actual constants are then listed,
separated by commas. The whole thing is closed with a semicolon. The code above does
not actually create any variables; it just creates the enumeration type called weekdays. To
create a variable of type weekdays do the following:
As you can see, this closely follows the way structures are used. In fact, as far as
the enumeration name goes, all the ways for naming structure types, and creating
variables of those structures, are also valid for enumerations.
The names in the body of the enumeration are all actually integers. In fact, once
declared the enumeration values can be used anywhere an integer is used. By default, the
values in the enum, start with zero, and increment by one. So the weekdays are: sunday =
0, monday = 1, tuesday = 2 etc.. In the definition you can specify if you want them to be
different. Simply set each item in the enum equal to what you want. For example:
enum weekdays {
sunday = 1,
monday = 2,
tuesday = 3,
wednesday = 4,
thursday = 5,
friday = 6,
saturday = 7
};
This will adjust the values to more intuitive numbers. You could put in any values
you want though. An easier way to do it is this:
enum weekdays {
sunday = 1,
monday,
tuesday,
wednesday,
thursday,
friday,
saturday
};
This will start the numbering at one, and the rest will fall in line. The only other
thing to mention about enum declarations is that once a symbolic value is declared for
one enumerated type, it cannot be used in another enumerated type (at least not in the
same scope).
return 0;
}
Once a typedef statement has been used, within the scope we can use the newly
defined type as good as a primitive type, though the usual restrictions based on the
operations supported by the primitive datatype or the structure we have created still hold
good.
8) Union
The concept of union is very useful in handling register operations in C which was
the original reason for introducing the concept. Unions can be useful when we do not
know the datatype we are going to deal with but have an idea of the domain of datatypes,
which we will be using.
#include<stdio.h>
union temp_union
{
int a;
char b;
};
int main()
{
union temp_union c;
c.a=10;
printf("%d",c.a);
c.b='a';
printf("%c %d",c.b,c.a);
return 0;
}
9) Graphics in C
The gdriver variable contains the driver number, this is set to DETECT ( a
macro defined in graphics.h)
The gmode variable is left uninitialized this will be set to the graphics mode
being used
errorcode = graphresult();
If the errorcode value is not equal to grOk, that means an error occurred in
initializing the graphics driver. A most common error would be that the egavga.bgi might
not be in the path specified as the third argument to the initgraph, otherwise things
would be fine.
This will close the graphics mode. This is important because if the graphics mode
is not exited properly with this function then the command shell might still remain in the
same mode causing problems for other programs running in the same shell.
The maximum resolution available using graphics.h and the EGAVGA.BGI file
would be about 640X480 with 16 colors. There is also a mode called mode 13 of the
graphics which is available , this mode has a 320X240 resolution but supports 256 colors.
Better graphics is not supported in C running in dos shell, WIN32 APIs is needed for
better graphics. Some open source libraries like Allegro (available in the internet) can be
used for better and easy graphics.