C Pgdca
C Pgdca
kn Unäv
PROGRAMMING IN C
& DATA STRUCTURE
STUDY MATERIAL
Version I
Study Material
Printed on 2023
by
CENTRE FOR DEVELOPMENT OF IMAGING TECHNOLOGY
(Under Government of Kerala)
©
Centre for Development of Imaging Technology (C-DIT)
This edition is authorised for Private Circulation Only
All rights reserved. This publication may not be reproduced in any way.
Programming in C & Data Structure 2
kn Unäv
PREFACE
CONTENTS
SI No Subjects Page No
Module I
Basics of Programming
1 Problem Solving using Computers
Problem solving is the process of identifying, analyzing, and resolving problems or obstacles
that arise in various situations. It involves defining the problem, gathering information, identifying
possible solutions, evaluating those solutions, and choosing the most effective course of action.
Effective problem solving requires critical thinking, creativity, and the ability to work collaboratively
with others. It is a skill that can be developed through practice and experience, and is essential for
success in many fields, including business, engineering, medicine, and law.
Problem solving through computers involves using computational tools and techniques to solve
problems. Programming is closely related to problem solving because programming involves designing
and implementing software solutions to solve problems. The process of programming often involves
identifying and analyzing problems, breaking them down into smaller, more manageable parts, and
then designing and implementing a solution using programming languages and tools. Programming
requires the ability to think critically and creatively, as well as the ability to communicate effectively
with computers through code. Programmers must also be able to work collaboratively with others,
including other programmers, designers, and stakeholders, in order to identify requirements, design
solutions, and implement them effectively.
1.1 General Steps invloved in Problem Solving
1. Define the problem
◦ Identify the specific problem that needs to be solved and clearly define it.
◦ Involves gathering information, data, or feedback from relevant sources.
2. Analyze the problem
◦ Break down the problem into smaller parts and analyze each part to gain a better
understanding of the problem.
◦ Use critical thinking and analytical skills to examine the problem from different angles.
3. Develop a solution
◦ Generate possible solutions to the problem by using different problem-solving techniques.
◦ Evaluate the pros and cons of each potential solution and choose solution that best
addresses the problem.
4. Implement the solution
◦ Put the chosen solution into action, using a well-planned implementation strategy.
5. Monitor and evaluate the results
◦ Track the implementation of the solution and evaluate its effectiveness.
◦ Make adjustments or modifications as necessary.
6. Document the process
◦ Keep a record of the problem-solving process, including the steps taken, the decisions
made, and the results achieved, for future reference and learning.
7 Programming in C & Data Structure
kn Unäv
Terminal (Start/ Stop) Represents the start and the end of a flowchart.
1.4 Software
Software is a collection of computer programs, data, and instructions that tell the computer how
to perform specific tasks. Software can be categorized into two main types.
• System software.
• Application software.
1.4.a System software
System software is a type of software is designed to control and manage the computer’s hardware
resources, such as the operating system, device drivers, and utility programs.
• Operating system : It manages the computer’s hardware and software resources, and provides
services and interfaces for applications to interact with the hardware. Examples of operating
systems include Linux, Microsoft Windows, macOS, and Unix.
• Device driver : Device drivers are software programs that facilitate communication between
the operating system and the hardware devices connected to the computer. They enable the
operating system to control and manage hardware components such as printers, scanners,
keyboards, and mice.
• Utility programs: They are software programs that are used to perform specific system
maintenance and management tasks, such as disk cleanup, system backup and restore, and
virus scanning. They help to ensure that the system runs smoothly and efficiently.
1.4.b Application software
Application Software is type of software that is designed to perform specific tasks or functions
such as word processing, spreadsheet analysis, database management, graphic design, video editing,
gaming, etc. for end-users. It is also known as end-user software or productivity software. Some
examples of popular application software include
• The office suite packages typically include software for word processing, spreadsheets, and
presentations. Examples include Libre Office, Microsoft Office
• Multimedia software lets users play create or record videos, audio files, and image files.
Common examples of multimedia software are VLC Player, MX Player, and Windows Media
Player.
• Web browsers are software applications used to browse the internet. The most popular web
browsers are Chrome and Firefox.
• Enterprise resource planning (ERP) is a software and a system that manages all the core
activities and other business processes of an organisation. Examples of ERP software are
Odoo, Oracle NetSuite, and SAP Business ByDesign.
• Database software is designed to create a database to store, search, manipulate, and extract
data. MySql, Postgres, and Oracle are some of the examples.
• A custom-developed application software is built solely for an organisation or an individual
user according to their business needs.
Many application software programs are also available as cloud-based services, which allow users
to access the software and their data from any device with an internet connection. Google Docs is an
example of this kind of software.
Programming in C & Data Structure 10
kn Unäv
1. Analyse the Problem: The step involves the identification of the desired output, determining
the inputs required to produce the desired output, and figuring out the processes required to
produce the desired output from the input.
2. Design a solution: In design phase, a detailed description of steps to solve the problem is
created. Typically, this description is known as algorithm.
3. Code the program: Once a workable solution to a problem has been developed, it must
be converted into computer program code using a programming language like C, C++, or
JavaScript. The term coding refers to this process.
4. Test the program: Testing the program ensures that the software is free of errors and that it
indeed solves the given problem. At this point, the programme is executed using various sets
of input data to determine if it is working properly.
5. Document and Maintain the program : After the program has been deployed, it will require
ongoing maintenance to ensure that it continues to function correctly. This includes fixing
bugs, adding new features, and updating the software to keep up with changes in technology.
Documentation is a way to provide additional explanation in a natural language like English,
which makes it easier for others to understand the program code or working of the program.
Normally, software provides internal and external documentation. Internal documentation
exists within the code and explains it. External documentation is provided separately from
the program in a user’s guide or maintenance manual
Consider the below given problem as an example.
A local store needs to develop a program to compute the total price of an item that is sale when
given the item’s price and quantity. The total price must include the GST (5%) as well.
• Analyse the problem : In this stage, the programmer will look for solutions to the following
queries in the following order at this stage.
◦ What is the required output?
◦ What is the necessary input?
◦ What is the process to obtain the required output from the given input ?
These three steps directly corresponds to the three main building blocks of most programs –
input/process/output.
In the above problem, the program needs to output the Total Price
The following inputs are necessary to produce the desired output
1. Machine language: Machine language is the lowest level of programming language and
consists of binary code that is directly executed by the computer’s processor. Each instruction
is represented by a sequence of 0s and 1s, and the computer executes these instructions in
sequence.
Example : 01100001 01100010 01100011 01100010 01100100
2. Assembly language: Assembly language is a low-level programming language that is slightly
easier to read and write than machine language. It consists of human-readable mnemonics that
correspond to machine language instructions. An assembler translates assembly language into
machine language.
Example :
mov eax, [num] ; move number into eax register
mov ebx, 1 ; set ebx to 1 for multiplication
cmp eax, 0 ; check if number is zero
je done ; if yes, jump to done
Low-level programming languages are used for system-level programming, such as developing
operating systems, device drivers, and firmware. They provide more direct control over the computer’s
resources, but are also more difficult to write and debug than higher-level programming languages.
A high-level programming language is a programming language that is designed to be easy for
humans to read and write, as opposed to low-level programming languages that are designed to be
executed directly by a computer. High-level programming languages are typically designed with a
focus on abstraction, so that programmers can write code that is closer to natural language and easier
to understand.
Examples of high-level programming languages include:
◦ Python: A popular scripting language that emphasizes simplicity and readability. Python
code is typically easier to read and write than code in lower-level languages, making it a
good choice for beginners.
◦ Java: A versatile language that is widely used for building enterprise-level applications,
mobile apps, and games. Java is known for its stability, portability, and robustness.
◦ C++: A powerful language that is used for developing high-performance software such as
video games, operating systems, and scientific simulations. C++ is known for its speed,
efficiency, and control over system resources.
◦ JavaScript: A language that is used primarily for developing web applications and dynamic
user interfaces. JavaScript is a popular choice for client-side programming, as it can be
executed directly in a web browser.
◦ PHP: A server-side scripting language that is used for building dynamic web applications
and content management systems. PHP is known for its ease of use and flexibility.
High-level programming languages are typically easier to learn and use than low-level programming
languages, but they may sacrifice some performance and control over system resources in the interest
of simplicity and readability.
◦ Better error checking: The compilation process involves thorough error checking, which
can catch many errors before the program is even executed.
◦ Code optimization: Compilers can perform various optimizations on the resulting machine
code to make it run more efficiently, which can result in faster and more efficient programs.
Disadvantages of compilers:
◦ Longer development time: Writing and testing code for a compiler can take longer than
for an interpreter, as the process involves additional steps such as compilation and linking.
◦ More complex: Compilers can be more complex than interpreters, as they need to perform
more operations during the compilation process.
◦ More difficult debugging: Debugging compiled code can be more difficult than debugging
interpreted code, as it can be harder to identify the source of errors.
References
1. Prelude to Programming, 6th edition, StewartVenit, Elizabeth Drake, Pearson Ed.
Module II
Introduction to C Programming
The C is a general purpose programmign language that was created in the early 1970s by Dennis
Ritchie at Bell Labs, who was also one of the developers of the Unix operating system. It is an
outgrowth of earlier two programming languages BPCL and B. ( In 1967, Martin Richards developed
a language called BCPL (Basic Combined Programming Language) which was derived from ALGOL.
In 1970, Ken Thompson created a language using many features of BCPL and called it simply B. C
was initially developed as a system programming language to be used for writing low-level operating
system code, and it was intended to be portable and efficient.
In 1978, Brian Kernighan and Dennis Ritchie published “The C Programming Language,” which
quickly became the definitive guide to the language and helped to popularize it. C rapidly gained
popularity in the 1980s as a language for developing software for personal computers and workstations.
In the early 1990s, the ANSI C standard was created to provide a standardized definition of the
language, which helped to improve its portability and compatibility across different systems. The
C99 and C11 standards were subsequently developed to add new features to the language and address
various issues.
Today, C remains a popular language for developing system software, embedded systems, and
other performance-critical applications due to its low-level capabilities and efficiency. Its influence
can be seen in many other popular programming languages, such as C++, Java, and Python.
2.1 Structure of a C program
Every C program consists of one or more modules called functions. (A function is a block of
code that performs a specific task.) One of the functions must be called main and serves as the entry
point of the program. When a C program is executed, the main() function is the first function that is
executed by the operating system. The main() function is responsible for coordinating the execution
of the program and typically contains the core logic of the program.
Some simple C program are given below.
Examples 1:
#include <stdio.h>
int main() {
printf(“Hello, world!\n”);
return 0;
}
• In this example, the program execution begins from the main() function and it prints the
message “Hello, world!” to the console using the printf() function and returns 0 to indicate
that the program completed successfully.
• The line #include<stdio.h> is a preprocessor directive in C that tells the compiler to include
the standard input-output library in the program. This library provides functions for performing
input and output operations in C programs, such as printing text to the console and reading
input from the user.
19 Programming in C & Data Structure
kn Unäv
• Curly braces { } are also used to define the body of a function in C. All statements that belong
to a function are enclosed within curly braces. Generally, curly braces { } are used to define
a block of code that belongs together. A block of code can contain one or more statements,
which are instructions that perform a specific task or action. By enclosing a set of statements
within curly braces, programmers can group them together and treat them as a single unit.
Examples 2:
/* Program to calculate the area of a circle.
Area = π r2 where π = 3.14159*/
#include <stdio.h>
// Define the value of PI
#define PI 3.14159
int main(){
int radius;
float area;
• In the above program, the statements inside /* */ and statements starting with // are called
comments. Comments provide a brief explanation or description of the code that follows. They
are used to make the code more readable and easier to understand, both for the programmer
who wrote the code and for other programmers who may need to read or modify the code
in the future. There are two types of comments in C: single-line comments and multi-line
comments. Single-line comments begin with // and continue until the end of the line, while
multi-line comments are enclosed within /* and */ and can span multiple lines.
• #define directive is used to define the value of PI as a symbolic constant. When the program
is actually compiled, the symbolic constant will automatically be replaced by its equivalent
numerical value.
Example 3
/* Program to calculate the area of a circle using a user defined function to calculate area
Area = π r2 where π = 3.14159*/
#include <stdio.h>
// Define the value of PI as a global constant
const float PI = 3.14159;
// Function declaration to calculate the area of a circle
float calculate_area(float radius);
int main(){
int radius;
float area;
// Prompt the user to enter the radius
printf(“Enter the radius of the circle:”);
scanf(“%d”, &radius);
// Call the calculate_area() function
area = calculate_area(radius);
// Output the result to the console
printf(“The area of the circle is %.2f\n”, area);
return 0;
}
// Function declaration to calculate the area of a circle
float calculate_area(int radius){
float area = PI * radius * radius;
return area;
}
• In this program, PI is defined as a constant global value using the const keyword. The
calculate_area() function takes a radius value as input and returns the calculated area of the
circle using the formula A = πr2. The main() function prompts the user to enter the radius of the
circle, calls the calculate_area() function to calculate the area, and then outputs the result to
the console. By using a user-defined function to calculate the area of the circle, the calculation
logic is seperated in a separate function and make the code more modular and reusable.
2.1 a General structure of a C program
A typical C program mainly consists of elements
• Preprocessor Directives: These are instructions that tell the compiler how to process the
code before compilation. They typically begin with the # symbol and include commands such
as #include and #define.
• Global Declarations: These are variables and functions that are used throughout the program
and are declared at the beginning of the program, before the main function.
• Main Function: This is the heart of the program and is the starting point for the program’s
execution. It contains statements that are executed one after the other, in the order in which
they are written.
• Statements: These are the individual instructions that make up the program. They can be
variable declarations, assignments, loops, conditionals, and other basic operations.
Programming in C & Data Structure 22
kn Unäv
• Functions: These are separate blocks of code that perform a specific task and can be called
from other parts of the program. Functions can have input parameters and can return values.
• Comments: These are non-executable statements that are used to provide information about
the program and improve its readability. They are ignored by the compiler. Documentation
section is also a set of comment lines which tells the details about the program such as the
name of the program, the author and other details.
2.2 Character set
A character set is a collection of characters and symbols that can be used to write programs.
The C character set includes uppercase and lowercase letters, digits, punctuation marks, and control
codes. The special characters and control codes represented a set of escape sequences. For example,
the escape sequence ‘\n’ represents a newline character, and the escape sequence ‘\t’ represents a
horizontal tab character.
2.3 C Tokens
In C programming language, a token is the smallest individual unit of a program that is meaningful
to the compiler.
2.4 Keywords
Keywords are reserved words in C that have standard, predefined meanings in C.The standard
keywords are given below.
auto break case char
const continue default do
double else enum extern
float for goto if
int long register return
short signed sizeof static
struct switch typedef union
unsigned void volatile while
2.5 Identifiers
Identifiers are names given to variables, functions, arrays, and other user-defined entities in a
program.
Rules for identifiers forming identifiers
◦ can consist of letters, digits, and underscores.
◦ first character must be an alphabet or underscore.
◦ can not be a keyword.
Example : s, names, y12, sum_12, _temp.
Note :Use lowercase for variable names and uppercase for symbolic constants. Local variable names
should be short and external names should be longer and more descriptive. Variable names can begin
with an underscore (_), but this should be avoided as such names, by convention, are reserved for
library implementations.
2.6 Literals
Literals are data used for representing fixed values.
• Integer constants: An integer literal can be a decimal, octal, or hexadecimal constant. A prefix
specifies the base or radix: 0x or 0X for hexadecimal, 0 for octal, and nothing for decimal.
Example :
85 /* decimal */
0213 /* octal */
0x4b /* hexadecimal */
• Real constants: A real literal has an integer part, a decimal point, a fractional part, and
an exponent part. The floating point literals can be represented either in decimal form or
exponential form.
Example :
3.14159 /* Decimal Form */
314159E-5L /* Exponential Form */
Programming in C & Data Structure 24
kn Unäv
• Character constants: Character literals are enclosed in single quotes. A character literal can
be a plain character (e.g., ‘x’), an escape sequence (e.g., ‘\t’), or a universal character (e.g., ‘\
u02C0’). There are certain characters in C that represent special meaning when preceded by a
backslash for example, newline (\n) or tab (\t).
• String literals: String literals or constants are enclosed in double quotes “ ”
Example :
“good” //string constant
“” //null string constant
“ ” //string constant of white space
“x” //string constant having a single character.
“Earth is round\n” //prints string with a newline
2.7 Data types
C is a typed language. Each variable is assigned a specific type which defines what values it can
represent, how its data is stored in memory, and what operations can be performed on it. There are
three basic types in the C language: characters, integers and floating-point numbers. The keywords
used in C to represent character type is char, integer type is int and floating point type is float and
double.
• int: Integers are whole numbers that can have both zero, positive and negative values but no
decimal values. The size of int is usually 4 bytes (32 bits). And, it can take 232 distinct states
from -2147483648 to 2147483647.
• float and double : float and double are used to hold real numbers. The size of float (single
precision float data type) is usually 4 bytes and the size of double (double precision float data
type) is 8 bytes. The float stores real numbers with precision upto 6 decimal places where as
double stores real numbers upto 15 decimal places.
• Char: Keyword char is used for declaring character type variables. The size of the character
variable is 1 byte.
• Type specifiers – long and short: To store larger or small numeric values, the type specifiers
long or short may be used.
Example :long int, long float, short int, etc
• Type modifiers – signed and unsigned: signed and unsigned are type modifiers. The data
storage of a data type can be altered by using them.
◦ signed - allows for storage of both positive and negative numbers
◦ unsigned - allows for storage of only positive numbers
Example : unsigned int age;
2.8 Variables
A variable is a named memory location that is used to store a value. A variable is declared with a
data type, which specifies the type of data that can be stored in the variable. Variable declaration does
two things:
25 Programming in C & Data Structure
kn Unäv
Here, the “.2” after the “%” sign specifies that the floating-point number should be printed with
two decimal places.
2.10 Formatted output: scanf
The scanf() function is used to read formatted input in C. The syntax for using the scanf() function
is as follows:
scanf(“format string”, &variable1, &variable2, ...);
Here, the “format string” specifies the format of the input that is expected, and the “&” symbol is
used to specify the memory location of the variables where the input will be stored.
For example, to read an integer value from standard input, the format string is “%d”. The following
code snippet shows how to use the scanf() function to read an integer value from standard input:
int num;
scanf(“%d”, &num);
Formatted input also allows reading multiple values at once. For example, to read an integer and
floating point values separated by a space, the format string is “%d %f”. The following code snippet
shows how to use the scanf() function to read an integer and floating point values:
int num1;
float num2;
scanf(“%d %f”, &num1, &num2);
2.11 Constants
A constant is a value that cannot be changed during program execution. Constants can be classified
into two types:
1. Numeric Constants: These are constants that represent a number, such as integers, floating-
point numbers, or even characters.
For example:
const int MAX = 100;
const float PI = 3.14;
const char NEWLINE = ‘\n’;
2. Symbolic Constants: Symbolic constants are defined using the #define preprocessor directive.
For example:
#define MAX 100
#define PI 3.14
#define NEWLINE ‘\n’
Symbolic constants are often used to define constants that are used frequently in the program, such
as the maximum size of an array or the value of pi. They make the code easier to read and maintain,
as they provide meaningful names instead of arbitrary values.
2.12 Operators
2.12 a Arithmetic operators
The arithmetic (or numerical) operators come in two varieties: unary and binary.
• Binary operators: Binary operators operate on two operands or values. They take two values
as input and return a single value as output. Binary operators in C are given below
Operation Operator x =10; y= 3; x=10; y= 2.5
Addition + x+y = 13 x+y = 12.5
Subtraction - x-y=7 x+y = 7.5
Multiplcation * x * y = 30 x*y = 25.0
Division / x/y=3 x/y = 4.0
Modulus % x %y = 1 Not defined
Note: The modulus operator is valid only for non-floating-point types (e.g., char, int, etc), and
x % y produces the remainder from the division x / y (e.g., 18 % 7 is equal to 4)
It is important to notice that integer division truncates any fractional part (e.g., 17/5 is equal
to 3).
• Unary operators: An operator that operates on a single operand is known as a unary operator.
◦ Unary plus and minus (+ and -): These operators are used to reverse the sign of a value.
For example, the expression -x will give the negative value of x.
◦ Increment and decrement operators (++ and --): These operators are used to increment
or decrement the value of a variable by 1. Thus, the expression x++ is equivalent to
x = x + 1. The ++ and -- may be used as prefix (++x) or postfix (x++) with different
characteristics.
For example,
double x = 3.2;
double y = ++x;
double z = x++;
In the first case, called preincrement, the value of x is increased to 4.2 and then assigned
to y, which then also equals 4.2. ie, it is equivalent to two statements x = x+1; and y = x;
In the second case, called postincrement, the value of x is first assigned to z, and
subsequently increased by 1; so, z equals 4.2 and x equals 5.2. ie, y= x; and x = x+1;
2.12 b Precedence of an operator
The precedence of an operator determines the order in which operations are evaluated in an
expression. The precedence of the arithmetic operators is as follows (operators with higher precedence
are listed first):
◦ Postfix increment and decrement: a++, a--
◦ Prefix increment and decrement: ++a, --a
Programming in C & Data Structure 28
kn Unäv
! Logical negation
~ Bitwise(1 's) complement
+ Unary plus
- Unary minus
++ Increment
Right to left
-- Decrement
& Dereference (Address)
* Pointer reference
sizeof Returns the size of an object
(type) Typecast (conversion)
* Multiply
/ Divide Left to right
% Remainder
+ Binary plus(Addition)
Left to right
- Binary minus(subtraction)
<< Left shift
Left to right
>> Right shift
< Less than
<= Less than or equal
Left to right
> Greater than
>= Greater than or equal
== Equal to
Left to right
!= Not equal to
& Bitwise AND Left to right
^ Bitwise exclusive OR Left to right
| Bitwise OR Left to right
&& Logical AND Left to right
|| Logical OR Left to right
?: Conditional Operator Right to left
= Simple assignment
*= Assign product
/= Assign quotient
%= Assign remainder
+= Assign sum
-= Assign difference Right to left
&= Assign bitwise AND
^= Assign bitwise XOR
|= Assign bitwise OR
<<= Assign left shift
>>= Assign right shift
, Separator of expressions Left to right
Example :
if (number < 0) {
number = - number;
}
printf(\n The absolute value = %d”, number);
33 Programming in C & Data Structure
kn Unäv
In this example, the program then checks if the number is negative using the condition number < 0.
If the condition is true, the program executes the block of code inside the if statement, which reverse
the sign of the number by multiplying with -1. Then it prints the absloute value of the number to the
console.
If the condition is false, the program skips the block of code inside the if statement and continues
executing the rest of the program. In this case, it prints the absloute value of the number to the console.
2.14 b if – else statement
The if – else statement is an extension of simple if statement.
Syntax :
if (condition) {
// code to be executed if the condition is true
} else {
// code to be executed if the condition is false
}
If the condition is true, the code within the first set of braces will be executed. If the condition is
false, the code within the second set of braces will be executed.
Example :
/* C program to check a person is eligible to vote in an election using if else
Criteria : if a person is eligible to vote if she/he is greater than or equal to 18 years. */
Programming in C & Data Structure 34
kn Unäv
#include <stdio.h>
int main(){
int age;
printf(“Enter your age :” );
scanf(“%d”, &age);
if(age>=18) {
printf(“You are eligible to vote\n”);
}
else {
printf(“You are not eligible to vote\n”);
}
printf(“Thank you.\n”);
}
If the user enters a value greater than or equal to 18, the test expression age >= 18 is evaluated to
true. Since the condition is true, the statements in the first set of braces are executed. Hence, You are
eligible to vote is displayed on the screen and else part is skipped.
If the user enters a value less than 18, the test expression age >= 18 is evaluated to false, skip the
statements inside the if block and the statements inside the else block are executed. Hence, You are
not eligible to vote is displayed on the screen.
2.14 c if – else – if statement
This structure allows the programmer to test multiple conditions and execute different blocks of
code depending on the outcome of each test.
Syntax :
if (condition1) {
// code block to execute if condition1 is true
}
else if (condition2) {
// code block to execute if condition2 is true
}
else if (condition3) {
// code block to execute if condition3 is true
}
...
else {
// code block to execute if all previous conditions are false
}
35 Programming in C & Data Structure
kn Unäv
If the condition1 is true, the code block immediately following the condition1 will be executed. If
the condition1 is false, execution will move on to the next “else if” block, which will test its own
condition, ie, condition2. This process goes on until a condition becomes true, or if none of the
conditions are true, the code block following the final “else” keyword will be executed.
It’s important to note that only the code block following the first condition that evaluates to true
will be executed. All other code blocks in the structure will be skipped over.
Example:
/* C program to check whether the given number is positive, negative or zero using if else if
statements */
Programming in C & Data Structure 36
kn Unäv
#include <stdio.h>
int main() {
int num;
printf(“Enter a number: ”);
scanf(“%d”, &num);
if (num< 0) {
printf(“The number is negative.\n”);
}
else if (num> 0) {
printf(“The number is positive.\n”);
}
else {
printf(“The number is zero.\n”);
}
return 0;
}
The above program uses the “if else if” structure to test the value of “num” and print a message
indicating whether it’s negative, positive, or zero. If “num” is less than zero, the code block following
the first “if” statement will be executed, printing the message “The number is negative.” If “num” is
not less than zero, the program will move on to the next condition, checking whether it’s greater than
zero. If it is, the code block following the second “if” statement will be executed, printing the message
“The number is positive.” If neither of these conditions is true, the program will move on to the final
“else” statement and execute its code block, printing the message “The number is zero.”
2.14 d Nesting of if statements
When a series of decisions are involved, the program may have to use more than one if - else
statement in nested form. The nesting of if mean an if statement inside another if statement.
Syntax:
if (condition1) {
// code block to execute if condition1 is true
if (condition2) {
// code block to execute if condition1 and condition2 are true
}
}
Example:
/* C program to input two numbers and check the numbers are positive or negative using
nested if */
#include <stdio.h>
int main() {
int num1, num2;
printf(“Enter two numbers: ”);
scanf(“%d %d”, &num1, &num2);
if (num1 > 0) {
if (num2 > 0) {
printf(“Both numbers are positive.\n”);
}
else {
printf(“The first number is positive, but the second number is not.\n”);
}
}
else {
if (num2 > 0) {
printf(“The second number is positive, but the first number is not.\n”);
}
else {
printf(“Both numbers are non-positive.\n”);
}
}
return 0;
}
This examples uses nested “if” statements to test the values of “num1” and “num2” and print a
message based on the outcomes of those tests.
If “num1” is greater than zero, the program will move to the inner “if” statement and test whether
“num2” is also greater than zero. If both conditions are true, the program will execute the code block
inside the inner “if” statement, printing the message “Both numbers are positive.” If “num2” is not
greater than zero, the program will execute the code block inside the inner “else” statement, printing
the message “The first number is positive, but the second number is not.”
If “num1” is not greater than zero, the program will move to the outer “else” statement and test
whether “num2” is greater than zero. If it is, the program will execute the code block inside the outer
“if” statement, printing the message “The second number is positive, but the first number is not.”
If “num2” is not greater than zero, the program will execute the code block inside the outer “else”
statement, printing the message “Both numbers are non-positive.”
2.14 e switch statement
The “switch” statement provides an alternative to using “if – else – if” statements and can make
the code more concise and easier to read. The “switch” statement allows to test the value of a variable
and execute different blocks of code based on the outcome of that test.
Syntax:
switch (expression) {
case value1:
// code block to execute if expression equals value1
break;
case value2:
// code block to execute if expression equals value2
break;
...
default:
// code block to execute if expression does not match any case
39 } Programming in C & Data Structure
kn Unäv
In this structure, the “switch” keyword is followed by a set of parentheses containing an expression
to test. The expression should evaluate to an integral type, such as an integer or character. The “case”
keyword is followed by a constant value that the expression is compared against. If the expression
matches the value, the code block immediately following the case statement will be executed. If the
expression does not match any of the case values, the code block following the “default” keyword
will be executed. The “break” statement is used to exit the switch statement once the appropriate code
block has been executed.
Example:
#include <stdio.h>
int main() {
int grade;
printf(“Enter your grade (0-100): ”);
scanf(“%d”, &grade);
switch (grade / 10) {
case 10:
case 9:
printf(“Your grade is A.\n”);
break;
case 8:
printf(“Your grade is B.\n”);
break;
case 7:
printf(“Your grade is C.\n”);
break;
case 6:
printf(“Your grade is D.\n”);
break;
default:
printf(“Your grade is F.\n”);
break;
}
return 0;
}
In this program, the user is prompted to enter a grade between 0 and 100, which is stored in the
“grade” variable. The program then uses the “switch” statement to test the value of “grade” and print
a message indicating the corresponding letter grade.
If “grade” is between 90 and 100, the program will execute the code block for the first “case”
statement, printing the message “Your grade is A.” If “grade” is between 80 and 89, the program will
execute the code block for the second “case” statement, printing the message “Your grade is B.” If
41 Programming in C & Data Structure
kn Unäv
“grade” is between 70 and 79, the program will execute the code block for the third “case” statement,
printing the message “Your grade is C.” If “grade” is between 60 and 69, the program will execute
the code block for the fourth “case” statement, printing the message “Your grade is D.” If “grade” is
outside the range of 60 to 100, the program will execute the code block for the “default” keyword,
printing the message “Your grade is F.”
2.15 Loop structures
These structures are used to repeat a block of code a certain number of times or until a certain
condition is met. for, while, and do-while are the different loop structures in C.
There are mainly two types of loops in C Programming:
1. Entry Controlled loops: In entry controlled loops the test condition is checked before entering
the main body of the loop. for Loop and while loop are entry-controlled loops.
2. Exit Controlled loops: In exit controlled loops the test condition is evaluated at the end of the
loop body. The loop body will execute at least once, irrespective of whether the condition is
true or false. do-while loop is exit controlled loop.
Syntax:
do {
// code block to execute
} while (condition);
In this structure, the “do” keyword is followed by a block of code inside curly braces. After the
block of code, the “while” keyword is followed by a condition in parentheses and a semicolon. The
loop will continue to execute as long as the condition is true.
Example:
#include <stdio.h>
int main() {
int i = 1;
do {
printf(“The value of i is %d\n”, i);
i++;
} while (i<= 10);
return 0;
}
In this program, a variable “i” is initialized to 1. The “do-while” loop executes the code block
inside the curly braces, which prints the value of “i” and increments it by 1. Then, the loop checks
whether “i” is less than or equal to10. If the condition is true, the loop repeats, and if it is false, the
loop exits.
2.16 Jump structures:
These structures are used to transfer control from one part of the program to another. The most
commonly used jump structures in C are the break and continue statements.
2.16 a break statement
The “break” statement is a control flow statement that is used to
terminate the execution of a loop or switch statement. When a “break”
statement is encountered in a loop or switch statement, the program
immediately exits the loop or switch statement and continues with the
next statement after the loop or switch. The “break” statement is useful
when you want to exit a loop or switch statement prematurely, based
on a specific condition.
Example:
#include <stdio.h>
int main() {
int i;
for (i = 1; i<= 10; i++) {
if (i == 5) {
break;
}
printf(“The value of i is %d\n”, i);
}
return 0;
}
In this program, a variable “i” is initialized to 0. The “for” loop executes 10 times, and for each
iteration, it prints the value of “i”. However, inside the loop, there is an “if” statement that checks
whether “i” is equal to 5. If the condition is true, the “break” statement is executed, which immediately
terminates the loop and continues with the next statement after the loop.
2.16 b continue statement
The “continue” statement is a control flow statement that is used to skip the current iteration of a
loop and continue with the next iteration. When a “continue” statement is encountered in a loop, the
program immediately skips the rest of the code in the loop for the current iteration and proceeds with
the next iteration.
Example:
#include <stdio.h>
int main() {
int i;
for (i = 1; i<= 10; i++) {
if (i == 5) {
continue;
}
Programming in C & Data Structure 46
kn Unäv
References
1. Programming with C, Byron S Gottfried, Schaum’s Outlines
2. Programming in ANSI C, McGraw Hill, E Balagurusamy
3.
Module III
C Arrays and Functions
3.1 Arrays
An array is a collection of similar data types that are stored in contiguous memory locations.
Arrays permits to store multiple values in a single variable.
Syntax of declaring an array variable;
data_type array_name[array_size];
where
• data_type is the data type of the elements in the array (e.g. int, float, char)
• array_name is the name given to the array
• array_size is the number of elements to store.
Example : To declare an array of integers
int my_array[5];
Accessing array elements: Each element of an array has an index number. The index starts at 0
for the first element and goes up to array_size-1 for the last element. For example, index number of
my_array, in the above example, is from 0 to 4.
The index number is used to access the elements of the array. For example, to access the third
element of the my_array array is
int x = my_array[2];
Assigning values Array elements: The values of arrays elements can be assigned at the time of
declaration or later in the program using index notation.
Example:
int my_array[3] = {1, 2, 3}; // initialize array elements with values 1, 2, and 3
or
int my_array[] = {1, 2, 3}; /* declares an array of size 3 initialize array elements with values 1, 2,
and 3 */
my_array[1] = 4; // change the value of the second element to 4
Input and Output of Array Elements: The following examples illustrate the input and output of
array elements
scanf(“%d”, &my_array[2]); // take input and store it in the 3rd element
scanf(“%d”, &my_array[i]); // take input and store it in the i+1th element
Programming in C & Data Structure 48
kn Unäv
Accessing array elements: To access an element in a multidimensional array, use the row and column
indices.
For example, to access the element in the second row and third column of myArray, we can use the
following syntax:
int x = myArray[1][2];
Assigning values Array elements: An example to initialise an array at the declaration is given below.
Nested braces are used to initialise the array elements.
int myArray[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
To change the value of element in the second row and third colum is given below.
myArray[1][2] = 10;
Input and Output of Two Dimensional Array Elements: Input and output of two-dimensional
arrays are done using nested loops to iterate over the rows and columns of the array.
Example:
//input the elements of a two dimensional array
for (int i = 0; i< 3; i++) {
for (int j = 0; j < 4; j++) {
scanf(“%d”, &myArray[i][j]);
}
}
// output the elements of the array in matrix form
The function definition is same as the first line of function definition or function heading.
3. Function Call: This is where the function is invoked with the necessary arguments. function_
name(argument1, argument2, ..., argumentN)
Example:
int s = sum( 10, 7 ) ;
Note : A parameter/formal parameter is a variable that is declared in a function’s definition and
used to represent a value that the function expects to receive. An argument/actual parameter, on the
other hand, is a value that is passed to a function when it is called.
3.3.a The working of a function
An illustration of the working of a function is given below.
Example:
#include <stdio.h>
void swap(int , int );
int main() {
int x = 5, y = 10;
swap(x, y);
printf(“x = %d, y = %d\n”, x, y);
return 0;
}
void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
}
In this example, the swap function takes two integer parameters a and b, which are passed by
value. Within the function, a temporary variable temp is used to swap the values of a and b. However,
since the variables are passed by value, the values of x and y are not changed outside the function.
Therefore, the output of the program is x = 5, y = 10.
The memory representation while executing the program is shown below. The address of the
variables are different in main function and swap function. ie, the memory address of variable a inside
the swap function is different from the address of the variable x in the main function. Similarly the
memory address of variable b inside the swap function is different from the address of the variable y
in the main function. So any changes in the variables inside swap function will not be reflected in the
main function.
Memory: (Before function call)
Address Variable Value
--------------------------------------
0x100 x 10
0x104 y 20
After calling swap(&x, &y);
Memory before returning to main function:
Address Variable Value
--------------------------------------
0x200 a 20
0x204 b 10
0x210 temp 10
Memory after swap function call:
Address Variable Value
--------------------------------------
0x100 x 10
0x104 y 20
2. Pass by reference: In this method, the address of the argument is passed to the parameter of
the function. The parameter is a pointer to the argument’s memory location, so any changes
made to the parameter inside the function affect the value of the argument outside the function.
Example:
#include <stdio.h>
void swap(int *, int *);
int main() {
int x = 5, y = 10;
swap(&x, &y);
printf(“x = %d, y = %d\n”, x, y);
return 0;
}
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
In this example, the swap function takes two integer pointers a and b, which are passed by reference.
Within the function, the values of the variables are swapped using pointers. Since the addresses of the
variables are passed by reference, the values of x and y are changed outside the function. Therefore,
the output of the program is x = 10, y = 5.
The memory representation while executing the program is shown below. The address of the
variables are same in both functions. ie, the memory address of variable a inside the swap function is
same as the address of the variable x in the main function. Similarly the memory address of variable
b inside the swap function is same as the address of the variable y in the main function. The variables
x and a are one and the same, even though they are referred by different names. Similarly y and b. So
any changes in variables a and b will be reflected in the variables x and y.
int main() {
int n = 4;
int result = factorial(n);
printf(“Factorial of %d is %d\n”, n, result);
return 0;
}
int factorial(int n) {
if (n == 0 ) {
return 1;
} else {
return n * factorial(n-1);
}
}
In this example, the factorial function takes an integer parameter n. If n is 0, the base case is
reached and the function returns 1. Otherwise, the recursive case is executed, which calls the factorial
function again with n-1 as the parameter. This process continues until the base case is reached.
From the above example, every recursive definition has
− Every recursive definition must have one (or more) base cases.
− The general case must eventually be reduced to a base case.
− The base case stops the recursion.
Recursive functions can be used to solve a wide range of problems, including searching, sorting,
and traversing data structures. However, recursive functions can have performance issues if not
implemented correctly, as they can consume a lot of memory and cause a stack overflow. Therefore,
it is important to carefully design recursive functions and ensure that they have a base case that will
eventually be reached.
3.3.d Passing Arrays to Functions
While passing arrays to the argument, only the name of the array is passed as an argument (i.e,
starting address of memory area is passed as argument). No need to specify the subscript operator []
along with the array name. In formal parameter, subscript operator [] is used along with array name.
Example:
/* Program to pass an array containing age of a person to a function. This function will return
average age and display the average age in main function. */
#include <stdio.h>
float average(float a[]);
int main() {
float avg, c[]={23.4, 55, 22.6, 3, 40.5, 18};
avg=average(c); /* Only name of array is passed as argument. */
printf(“Average age=%.2f”,avg);
return 0;
}
float average(float a[]){
int i;
float avg, sum=0.0;
for(i=0;i<6;++i){
sum+=a[i];
}
avg = (sum/6);
57 Programming in C & Data Structure
kn Unäv
return avg;
}
3.4 Storage Class
Every variable in C programming has two properties: data type and storage class.
Data type refers to the type of information represented by a variable, e.g. interger, character,
floating point number, etc.
Storage class determines the following attributes of a variable
━ Scope : defines where the variable is accessable in the program.
━ Default definition value : a random value or zero.
━ Lifetime : how long the variable stays in existence.
━ Location of storage : whether in RAM or register.
There are four different storage class specifications in C
Storage class keyword
automatic auto
external extern
static static
register register
• Automatic Variables : The variables declared inside a block (function) are automatic or
local variables. ( A block is basically the code between an opening brace and a closing brace.
So a function body is a block.). Any variable declared within a block or function is interpreted
as automatic variable unless a different storage class specified in the declaration.
A keyword auto is used to define an auto storage class, but it is optional.
e.g. : auto int max; // Explicit declaration
int min; // By default auto
− Scope : within block/function
− Default definition value : random(garbage) values by default.
− Lifetime : during the exection of function / block
− Location of storage : RAM.
Example:
#include <stdio.h>
void foo() {
int x = 5; // automatic storage class
printf(“%d\n”, x);
}
Programming in C & Data Structure 58
kn Unäv
int main() {
foo();
return 0;
}
In this example, the variable x has automatic storage class because it is a local variable inside
the function foo. The variable is created when the function foo is called and destroyed when
the function exits.
• External Variables : The variables that are declared outside of all functions are known as
external or global variables. Their scope extends from the point of definition to the reminder
of the program, often an entire program.
− Scope : global/ entire file
− Default definition value : 0.
− Lifetime : during the exection of program.
− Location of storage : RAM.
Since global variables retain their values, their values can be modified or accessed in different
functions. Thus these variables provide a convenient mechanism for transferring values
between functions.
The extern keyword is used to declare a global variable, but it is optional since the these
variables are identified by their location of definition within the program
Example:
#include <stdio.h>
extern int x; // external storage class
void foo() {
printf(“%d\n”, x);
}
int main() {
x = 5;
foo();
return 0;
}
In this example, the variable x is declared with external storage class outside of any function.
The variable is defined in the main function and initialized to 5. The variable can be accessed
by the function foo because it has external storage class.
• Static Variables : The static variables are limited the function in which they are defined, but
retains their values throughout the program.
− Scope : within the function in which they are defind
59 Programming in C & Data Structure
kn Unäv
Multifile Programs : C programs may be composed of multiple files. In multi file programs rules
associated with the use of storage classses are different.
− Functions : Within a multifile program, a function may be either static or external. An
external function will be recognised throughout the entire program, whereas a static
function will be recognised only within the file in which it is defined. Storage class
specification is placed at the beginning of the function definition.
Storage-class data-type name (type_1 arg_1, type_2 arg_2, ...., type_narg_n)
However if no storage class specifiction is not mentioned, then it is assumed as external
storage class .
− Variables : In variable declaration memory allocation does not happen whereas in
definition memory gets allocated to the variable. A declaration is just a reference for the
compiler that a variable or function is somewhere in the program or in another file to get
our program successfully compiled. Whereas in definition the compiler allocate memory
as per variable data type and it gets initialized to some default value as per the storage
class if it is not explicitly initialized by the programmer.
In a multifile program, the external variable can be defined in one file and accessed
in another. In order to access an external variable in another file, the variable must be
declared within that file and declaration must begin with storage class specifier extern.
Initial values can’t be included in external variable declarations.
3.5 Strings
A string is a sequence of characters. Strings in C are represented as arrays of characters, with the
last element of the array being the null character(\0).
An example of how to declare and initialize a string:
char string_name[] = “Hello, world!”;
This declares a character array named string_name and initializes it with the string “Hello world!”.
The null character is automatically added to the end of the string. The size of the array is automatically
determined by the compiler based on the length of the string plus one for the null character.
A sring can also be initialised as follows,
char array_name[] = {‘H’, ‘e’, ‘l’, ‘l’, ‘o’, ‘ ‘, ‘ W’,’ o’,’ r’,’’l’,’ d’,\0’};
3.5.a String functions in C
C provides several standard library functions for working with strings, including:
• strlen: returns the length of a string.
• strcpy: copies one string to another.
• strcat: concatenates two strings.
• strcmp: compares two strings.
61 Programming in C & Data Structure
kn Unäv
In the main function, a character array str is declared to hold the user’s input. The user is prompted
to enter a string and the string is read using the scanf function. Then, the isPalindrome function is
called with the input string passed as an argument. If the function returns 1, a message is printed
indicating that the string is a palindrome. Otherwise, a message is printed indicating that the string is
not a palindrome.
A program to find the occurenance of a given character in a string.
#include <stdio.h>
int countOccurrences(char *str, char ch);
int main() {
char str[] = “Programming in C and Data Structure”;
char ch = ‘a’;
int count = countOccurrences(str, ch);
printf(“The character ‘%c’ occurs %d times in the string.\n”, ch, count);
return 0;
}
int countOccurrences(char *str, char ch) {
int count = 0;
while (*str != ‘\0’) {
if (*str == ch) {
count++;
}
str++;
}
return count;
}
Output:
The character ‘a’ occurs 4 times in the string.
In this program, a string variable named str and a character variable named ch are declared. The
countOccurrences function is called passing str and ch as arguments.
The countOccurrences function accepts a pointer to a character array named str and a character
named ch as arguments. It initializes a count variable to 0 and loops through each character in the
string using a while loop. If the current character in the string matches the character ch, the count
variable is incremented. Finally, the function returns the count variable.
In the main function, the return value of the countOccurrences function is stored in an integer
variable named count.
References
1. Programming with C, Byron S Gottfried, Schaum’s Outlines
2. Programming in ANSI C, McGraw Hill, E Balagurusamy
Programming in C & Data Structure 64
kn Unäv
Module IV
Pointers and File
4
handling in C
4.1 Pointers
A pointer is a variable that stores the memory address of another variable. Pointers provide a
way to indirectly access and manipulate memory locations, and are commonly used in C to achieve
various programming tasks such as dynamic memory allocation, passing arguments by reference to
functions, and creating complex data structures.
4.1.a Declaration of a pointer variable
To declare a pointer variable in C, the asterisk symbol (*) is used before the variable name.
Syntax:
datatype *variable_name;
For example, the following code declares a pointer variable p that points to an integer:
int *p;
4.1.b Assigning value to a pointer variable
To assign a value to a pointer variable, the ampersand symbol (&) is used to obtain the memory
address of another variable. ‘&’ is called address of operator. For example, the following code assigns
the memory address of the integer variable x to the pointer variable p:
int x = 10;
p = &x;
4.1.c Accessing the value of a pointer variable
To access the value of the variable that a pointer points to, the asterisk (*) symbol is used again. `*`
is called indirection operator. For example, the following code prints the value of the integer variable
that p points to:
printf(“%d”, *p);
Example:
#include <stdio.h>
int main() {
int num = 10;
int *ptr = #
printf(“Value of num: %d\n”, num);
printf(“Address of num: %p\n”, &num);
printf(“Value of ptr: %p\n”, ptr);
printf(“Value pointed by ptr: %d\n”, *ptr);
*ptr = 20;
3. Hard to debug: Pointer-related bugs can be hard to track down and fix, especially in large
programs with complex data structures.
4. Null pointers: Using null pointers, which point to no memory location, can cause program
crashes and undefined behavior if not properly checked.
4.2 Arithmetic operations on pointers
Pointer arithmetic involves performing arithmetic operations on pointers to manipulate their
values. Here are some examples:
Adding/Subtracting an integer to a pointer: Adding or subtracting an integer value to a pointer
to move it to a new memory location. The integer value is multiplied by the size of the data type the
pointer is pointing to.
ie, Next Location = Current Location ± i * size_of(data type) where i is value added/subtracted.
Example:
#include<stdio.h>
int main(){
int no = 10;
int *ptr;
ptr = &no;
printf(“Current address no = %p \n”,&no);
printf(“Current address ptr = %p \n”,ptr);
ptr++;
printf(“Address after increment = %p \n”,ptr);
ptr--;
printf(“Address after decrement = %p \n”,ptr);
ptr = ptr + 3;
printf(“Address after Adding 3 = %p \n”,ptr);
ptr = ptr - 3;
printf(“Address after Subtracting 3 = %p \n”,ptr);
return 0;
}
Output:
Current address no = 0x7ffcc6d664fc
Current address ptr = 0x7ffcc6d664fc
Address after increment = 0x7ffcc6d66500
Address after decrement = 0x7ffcc6d664fc
Syntax:
struct structName {
dataType1 member1;
dataType2 member2;
// more members...
};
Here, structName is the name of the structure, and member1, member2, and so on are the names of
the members of the structure. The data types of the members can be any valid C data type, including
other structures, pointers, and arrays.
An example of a structure that represents a person’s information is given below.
struct Person {
char name[20];
int age;
float height;
};
4.4.b Defining a Structure variable
A structure variable can be defined by using the keyword struct followed by tag name followed by
structure variables
Syntax:
struct structName variableName;
Example:
struct Person p1, p2;
Now, p1 and p2 are variables of type Person
4.4.c Accessing Structure elements
Structure elements are accessed by using . (dot) operator.
Syntax:
variableName.memberName;
An example showing the method of accessing structure elements is given below.
strcpy(p1.name, “John”);
p1.age = 25;
p1.height = 5.8;
printf(“Name: %s\n”, p1.name);
printf(“Age: %d\n”, p1.age);
printf(“Height: %.1f\n”, p1.height);
69 Programming in C & Data Structure
kn Unäv
Syntax:
struct structName variableName[size];
Example :
struct Person people[3];
The following program illustrates the working of array of structure
#include <stdio.h>
struct Person {
char name[20];
int age;
float height;
};
int main() {
struct Person people[3] = {
{“Alice”, 25, 1.65},
{“Bob”, 30, 1.80},
{“Charlie”, 35, 1.75}
};
for (int i = 0; i< 3; i++) {
printf(“Person %d: %s, %d years old, %.2f meters tall\n”,
i+1, people[i].name, people[i].age, people[i].height);
}
return 0;
}
Output:
Person 1: Alice, 25 years old, 1.65 meters tall
Person 2: Bob, 30 years old, 1.80 meters tall
Person 3: Charlie, 35 years old, 1.75 meters tall
4.7 union
A union is a user-defined data type that allows you to store different data types in the same memory
location. A union is similar to a structure, but with a few key differences.
When you declare a union, you define a set of member variables, each with its own data type.
However, all of the members of a union share the same memory location. This means that a union
variable can hold only one of its members at a time.
Syntax:
union union_name {
member_type member_name1;
member_type member_name2;
...
member_typemember_nameN;
};
An example of a union that can store either an integer or a float is given below
union Number {
int i;
float f;
};
int main() {
union Number n;
n.i = 10;
printf(“n.i = %d\n”, n.i);
n.f = 3.14;
printf(“n.f = %f\n”, n.f);
printf(“n.i = %d\n”, n.i);
return 0;
}
In this example, we define a union called Number that can hold either an integer or a float. We
declare a variable of type Number called n and set its integer member to 10. We then print out the
value of n.i, which is 10.
Next, we set the float member of n to 3.14 and print out its value. We then print out the value of n.i
again, and we see that it has been overwritten with the binary representation of 3.14. This illustrates
the fact that all members of a union share the same memory location.
4.8 Files in C
A file is a collection of related data stored on a permanent storage device, such as a hard disk or
flash drive. The data in a file can be read and written by a program, allowing the program to access
and manipulate the information.
4.8.a Classification of files
Files can be classified in different ways. Some of the common classification are given below.
In addition, each mode can be modified with the following characters to indicate the type of file
being opened:
Mode Modifier Description
"b" Open binary file
"t" Open text file
For example, “rb” would indicate opening a binary file for reading, while “wt+” would indicate
opening a text file for reading and writing, truncating the file to zero length if it already exists.
Example:
The following program illustrates the basic file processing in C
#include <stdio.h>
int main() {
FILE *fp;
char filename[] = “example.txt”;
char buffer[100];
// open file for writing
fp = fopen(filename, “w”);
// write some data to the file
fprintf(fp, “This is some text\n”);
fprintf(fp, “And here is some more\n”);
// close the file
fclose(fp);
// open file for reading
fp = fopen(filename, “r”);
4.10 Macros
A macro is a fragment of code that is given a name and can be used like a function. Macros are
defined using the #define preprocessor directive, followed by the macro name and the replacement
text.
An example of a simple macro that defines the constant value of pi:
#define PI 3.14159
With this macro, any occurrence of the identifier PI in the code will be replaced by the value
3.14159 at compile time. For example, the following code snippet:
double area = PI * r * r;
gets transformed into:
double area = 3.14159 * r * r;
Macros can also take arguments, which allows them to be used for more complex tasks. Here is an
example of a macro that calculates the square of a number:
#define SQUARE(x) ((x) * (x))
When the preprocessor encounters code that calls the SQUARE macro, it replaces it with the
corresponding code, substituting the argument for the parameter x. For example, the following code
snippet:
int x = 5;
int y = SQUARE(x);
gets transformed into:
int x = 5;
int y = ((x) * (x));
before the code is compiled.
References
1. Programming with C, Byron S Gottfried, Schaum’s Outlines
2. Programming in ANSI C, McGraw Hill, E Balagurusamy
Module V
Data Structures using C
5
A data structure is a way of organizing and storing data in a computer program so that it can be
accessed and manipulated efficiently. It provides a standard way to represent, store and manipulate
complex data types.
There are several types of data structures in computer science including arrays, linked lists, stacks,
queues, trees, graphs, and hash tables. Each data structure has its own advantages and disadvantages
and is suited for different types of problems.
if (found == 1) {
printf(“%d is found at index %d.\n”, key, mid);
}
else {
printf(“%d is not found in the array.\n”, key);
}
return 0;
}
5.3 Sorting
Sorting algorithms are algorithms that arrange elements in a certain order, usually in ascending
or descending order. There are various sorting algorithms available, each with its own strengths and
weaknesses. Here are some commonly used sorting algorithms:
1. Bubble Sort: This algorithm compares adjacent elements and swaps them if they are in the
wrong order. The algorithm repeats this process until no more swaps are required.
2. Selection Sort: This algorithm selects the smallest element and places it at the beginning of
the array. It then selects the next smallest element and places it in the second position, and so
on, until all elements are sorted.
3. Insertion Sort: This algorithm starts with the second element and compares it with the first
element. If it is in the wrong order, it is swapped with the first element. The algorithm then
proceeds to the third element and repeats the process until all elements are sorted.
4. Merge Sort: This algorithm uses the concept of divide and conquer. It divides the array into
two halves, sorts each half separately, and then merges the sorted halves.
5. Quick Sort: This algorithm also uses the concept of divide and conquer. It selects a pivot
element and partitions the array around the pivot, with all elements smaller than the pivot
to the left and all elements larger than the pivot to the right. It then sorts the left and right
partitions separately.
6. Heap Sort: This algorithm sorts the array by first creating a heap from the elements, and then
repeatedly extracting the maximum element from the heap and placing it at the end of the
array.
5.3.a Insertion sort
Insertion sort is a simple sorting algorithm that works by iteratively building up a sorted list. It
works by sorting one element at a time, starting from the beginning of the list.
The basic idea behind insertion sort is to take each element from the list, and insert it into its
correct position in the sorted list. To do this, compare the current element with the elements already
in the sorted list, and insert it in the correct position by shifting the larger elements to the right.
The step-by-step process for insertion sort:
1. Start with the second element in the list, and assume that the first element is already sorted.
2. Compare the second element with the first element. If the second element is smaller, swap
them.
3. Move on to the third element, and insert it in the correct position in the sorted list by comparing
it with the elements already in the sorted list and shifting the larger elements to the right.
Repeat this process for all the remaining elements in the list.
The pseudocode for insertion sort is given below.(A is the array and length(A) is the length of the
array.)
for i = 1 to length(A) - 1
key = A[i]
j=i-1
while j >= 0 and A[j] > key
A[j+1] = A[j]
j=j-1
end while
A[j+1] = key
end for
An example of insertion sort algorithm in action is given below.
Consider an example: [5, 3, 4, 2, 1]
1. Start with the second element (i.e., index 1) and compare it with the first element (i.e., index
0). If the second element is smaller, swap it with the first element.
◦ Array after first pass: [3, 5, 4, 2, 1]
2. Move to the third element (i.e., index 2) and compare it with the elements before it (i.e., index
1 and 0). If the third element is smaller than any of the previous elements, shift those elements
to the right and insert the third element in its correct position.
◦ Array after second pass: [3, 4, 5, 2, 1]
3. Move to the fourth element (i.e., index 3) and compare it with the elements before it (i.e., index
2, 1, and 0). If the fourth element is smaller than any of the previous elements, shift those
elements to the right and insert the fourth element in its correct position.
◦ Array after third pass: [2, 3, 4, 5, 1]
4. Move to the fifth element (i.e., index 4) and compare it with the elements before it (i.e., index
3, 2, 1, and 0). If the fifth element is smaller than any of the previous elements, shift those
elements to the right and insert the fifth element in its correct position.
◦ Array after fourth pass: [1, 2, 3, 4, 5]
Therefore, the sorted array using insertion sort is [1, 2, 3, 4, 5].
5.3.b Merge sort
Merge sort is a divide-and-conquer algorithm that involves dividing an array into two halves,
sorting each half, and then merging the two halves back together. The basic idea behind merge sort is
to repeatedly merge two sorted subarrays into a larger, sorted subarray until the entire array is sorted.
The merge sort algorithm can be described in the following steps:
1. Divide the unsorted array into two halves (left and right) at the middle index.
2. Recursively sort the left half using the merge sort algorithm.
3. Recursively sort the right half using the merge sort algorithm.
4. Merge the two sorted subarrays back together to form the final sorted array.
The merge step works by comparing the first elements of the two subarrays and selecting the
smaller one to add to the merged array. This process is repeated until one of the subarrays is exhausted,
at which point the remaining elements of the other subarray are added to the merged array.
The pseudocode for the merge sort algorithm is given below.
mergeSort(arr[], l, r)
if l < r
1. Find the middle point to divide the array into two halves:
middle = (l + r) / 2
2. Call mergeSort for first half:
mergeSort(arr, l, middle)
3. Call mergeSort for second half:
mergeSort(arr, middle+1, r)
4. Merge the two halves sorted in step 2 and 3:
merge(arr, l, middle, r)
Example of the merge sort algorithm applied to the array [5, 3, 4, 2, 1]:
1. Splitting: The array is recursively divided into two halves until the size becomes 1 or 0. In this
example, the array is divided into two halves [5, 3] and [4, 2, 1].
[5, 3, 4, 2, 1] -> [5, 3] [4, 2, 1]
2. Splitting and Merging: The two halves are further divided into sub-halves until the size
becomes 1 or 0. Then, they are merged back into a sorted array. In this example, the sub-
halves are [5] and [3] and [4] [2] [1], and they are merged back into [3, 5] and [1, 2, 4].
[5, 3] -> [5] [3] -> [3, 5]
[4, 2, 1] -> [4, 2] [1] -> [4] [2] [1] -> [2, 4] [1] -> [1, 2, 4]
3. Merging: The two sorted sub-arrays are merged back into a single sorted array. In this example,
the two sorted sub-arrays [3, 5] and [1, 2, 4] are merged back into [1, 2, 3, 4, 5].
[3, 5] [1, 2, 4] -> [1, 2, 3, 4, 5]
4. Result: The final sorted array is [1, 2, 3, 4, 5].
Four passes through the list are completed. The list is now sorted in ascending order
5.3.d Selection sort
Selection sort is an algorithm that works by repeatedly finding the minimum element from the
unsorted part of the list and moving it to the beginning of the list. This algorithm maintains two
sublists, one which is already sorted and the other is unsorted. The algorithm works by selecting the
smallest element from the unsorted sublist and swapping it with the leftmost unsorted element, and
moving the sublist boundaries one element to the right.
The pseudocode for selection sort is
for i = 0 to n-1
min_index = i
for j = i+1 to n
if A[j] < A[min_index]
min_index = j
swap A[i] and A[min_index]
Here’s how selection sort works step-by-step with the given array [5, 3, 4, 2, 1]:
1. First, find the smallest element in the array. In this case, it is 1.
2. Swap the smallest element with the first element of the array. The array now becomes [1, 3,
4, 2, 5].
3. Repeat steps 1 and 2 for the remaining unsorted part of the array, i.e., [3, 4, 2, 5].
4. Find the smallest element in the unsorted part of the array, which is 2.
5. Swap the smallest element with the first element of the unsorted part of the array. The array
now becomes [1, 2, 4, 3, 5].
6. Repeat steps 4 and 5 for the remaining unsorted part of the array, i.e., [4, 3, 5].
7. Find the smallest element in the unsorted part of the array, which is 3.
8. Swap the smallest element with the first element of the unsorted part of the array. The array
now becomes [1, 2, 3, 4, 5].
9. Repeat steps 7 and 8 for the remaining unsorted part of the array, i.e., [4, 5].
10. Find the smallest element in the unsorted part of the array, which is 4.
11. Swap the smallest element with the first element of the unsorted part of the array. The array
now becomes [1, 2, 3, 4, 5].
12. Repeat steps 10 and 11 for the remaining unsorted part of the array, i.e., [5].
13. Find the smallest element in the unsorted part of the array, which is 5.
14. Swap the smallest element with the first element of the unsorted part of the array. The array
now becomes [1, 2, 3, 4, 5].
Stack underflow and overflow are two common errors that can occur when working with stacks.
1. Stack underflow: A stack underflow error occurs when a pop operation is performed on an
empty stack. In other words, when there are no elements in the stack, but we are trying to
remove an element from it.
For example, suppose we have an empty stack, and we try to pop an element from it: | |
After attempting to pop an element from the stack, we get a stack underflow error.
2. Stack overflow: A stack overflow error occurs when we try to add an element to a stack that
is already full. In other words, when we are trying to push an element onto the stack, but there
is no more space to add new elements. For example, suppose we have a stack of size 3, and
we try to push 4 elements onto it: | 3 | | 2 | | 1 |.
After attempting to push the fourth element (let’s say 4) onto the stack, we get a stack overflow
error.
To avoid these errors, we need to make sure that we always check the size of the stack before
performing any push or pop operations.
5.5 Queue
A queue is a linear data structure that follows the First In First Out (FIFO) principle. It is similar
to a real-life queue or line where the first person who joins the line is the first person to be served.
The queue has two primary operations:
1. Enqueue: This operation adds an element to the rear end of the queue.
2. Dequeue: This operation removes an element from the front end of the queue.
Other operations that can be performed on a queue include:
1. Peek or Front: This operation returns the frontmost element of the queue without removing
it.
2. Size: This operation returns the number of elements in the queue.
3. isEmpty: This operation returns a Boolean value indicating whether the queue is empty or
not.
Queues can be implemented using an array or a linked list. In an array implementation, the front
and rear ends of the queue are represented by two indices, while in a linked list implementation, the
front and rear ends are represented by the head and tail of the linked list, respectively.
Here’s an example to illustrate the working of the queue data structure:
Suppose we have a queue of customers waiting in line at a grocery store, and we want to perform
the following operations:
1. Three customers join the line.
2. The first customer in line is served and removed from the queue.
3. Two more customers join the line.
4. The next two customers in line are served and removed from the queue.
5. We check the number of customers remaining in the line.
Here’s how the queue would look like after each operation:
1. Initially, the queue is empty:
◦ front -> null rear -> null
2. After three customers join the line, the queue becomes:
◦ front -> customer1 -> customer2 -> customer3 <- rear
3. After the first customer is served and removed from the queue, the frontmost customer (i.e.,
customer1) is removed:
◦ front -> customer2 -> customer3 <- rear
4. Two more customers join the line, and they are added to the rear end of the queue:
◦ front -> customer2 -> customer3 -> customer4 -> customer5 <- rear
5. After the next two customers are served and removed from the queue, the frontmost
customers (i.e., customer2 and customer3) are removed:
◦ front -> customer4 -> customer5 <- rear
6. Finally, we check the number of customers remaining in the queue, which is 2:
◦ front -> customer4 -> customer5 <- rear
7. So, the final state of the queue is:
◦ front -> customer4 -> customer5 <- rear
5.6 Linked List
A linked list is a linear data structure that consists of a sequence of nodes, each containing a data
field and a reference (or pointer) to the next node in the sequence. Unlike arrays, linked lists don’t
have a fixed size, and their size can grow or shrink dynamically during program execution.
The first node in the sequence is called the head of the linked list, and the last node is called the
tail. The tail node’s reference is typically set to NULL to indicate the end of the list.
Linked lists can be used to implement various data structures, including stacks, queues, and hash
tables. There are three types of linked lists:
1. Singly linked list: In this type of linked list, each node contains a data field and a reference to
the next node in the sequence.
2. Doubly linked list: In this type of linked list, each node contains a data field, a reference to
the previous node, and a reference to the next node.
3. Circular linked list: In this type of linked list, the last node’s reference points back to the first
node, creating a circular sequence.
Linked lists have some advantages over arrays, including dynamic size allocation and efficient
insertion and deletion of elements. However, they also have some disadvantages, including slower
access time for random elements and extra memory overhead for storing references to the next nodes.
Working of Singly Linked List
Suppose we have a singly linked list that contains the following elements:
4. B-Tree: B-tree is a self-balancing tree data structure that can be used to store large amounts of
data on disk or other storage devices.
Here’s an example of a binary tree to illustrate the concept:
1
/ \
2 3
/\ /\
4 56 7
In this binary tree, the root node has a value of 1, and its left and right child nodes have values 2
and 3, respectively. The left child node has two child nodes with values 4 and 5, and the right child
node has two child nodes with values 6 and 7.
To traverse a tree, there are several algorithms, including depth-first search (DFS) and breadth-first
search (BFS). DFS can be performed using pre-order, in-order, and post-order traversal techniques,
while BFS visits all the nodes of a level before moving to the next level.
Trees have several advantages, including efficient insertion and deletion of nodes, easy traversal,
and easy representation of hierarchical structures. However, they also have some disadvantages,
including extra memory overhead and slower search time compared to other data structures like
arrays and hash tables.
4
/ \
2 5
/\ /\
1 36 7
Preorder Traversal: 4 2 1 3 5 6 7
3. Postorder traversal: In this traversal, we first visit the left subtree, then the right subtree, and
finally the root node. In other words, the left child is visited first, followed by the right child
and then the parent node.
Here’s an example postorder traversal of the binary tree shown in the previous answer:
4
/ \
2 5
/\ /\
1 36 7
Postorder Traversal: 1 3 2 6 7 5 4
These traversal methods are used in many applications of binary trees, such as finding the minimum
or maximum element, building an expression tree, or printing the nodes in a specific order.
5.8 Graph
A graph is a non-linear data structure that consists of a collection of nodes, also called vertices, and
a set of edges that connect the nodes. A graph is used to represent a network or a relationship between
entities, where the nodes represent the entities and the edges represent the relationships between them.
A graph can be directed or undirected, depending on whether the edges have a specific direction or
not. In a directed graph, the edges have a specific direction, and each edge connects a source node to a
destination node. In an undirected graph, the edges do not have a specific direction, and they connect
two nodes bidirectionally.
There are several types of graphs, including:
1. Weighted graph: A weighted graph is a graph in which each edge has a weight or a cost
associated with it. The weight can represent the distance between two nodes, the time it takes
to travel between two nodes, or any other measure of similarity or dissimilarity between two
nodes.
2. Connected graph: A connected graph is a graph in which there is a path between any two
nodes. In other words, every node in the graph is reachable from any other node.
3. Tree: A tree is a connected, acyclic graph, where each node has exactly one parent node,
except for the root node, which has no parent.
4. Bipartite graph: A bipartite graph is a graph in which the nodes can be partitioned into two sets
such that no two nodes within the same set are connected by an edge.
5. Complete graph: A complete graph is a graph in which each pair of nodes is connected by an
edge.
Graphs can be represented in several ways, including adjacency matrix and adjacency list. In an
adjacency matrix, the graph is represented as a two-dimensional matrix of size N x N, where N is the
number of nodes in the graph. The rows and columns represent the nodes, and the matrix elements
represent the edges. In an adjacency list, the graph is represented as an array of lists, where each
element in the array represents a node, and the list associated with the element contains the nodes that
are adjacent to it.
Graphs can be used in many applications, such as social networks, transportation networks,
computer networks, and many more. They are used in algorithms such as Dijkstra’s algorithm for
finding the shortest path between two nodes, and Kruskal’s algorithm for finding the minimum
spanning tree of a weighted graph
Examples:
A common example of a graph is a social network. In a social network, the nodes represent the
users, and the edges represent the relationships between them. For example, suppose we have a
social network with four users: Alice, Bob, Carol, and Dave. The relationships between them can be
represented as a graph, as follows:
A - B
| |
C - D
In this graph, each node represents a user, and the edges represent the relationships between them.
For example, there is an edge between Alice and Bob, which represents that they are friends. One
application is to suggest a friend in the above example is to recommend a friend of a friend. For
example, if we want to suggest a friend for Alice, we can look at Alice’s friends (Bob and Carol) and
suggest a friend of theirs who is not already friends with Alice. Suppose that Bob has a friend named
Eve, and Carol has a friend named Frank. We can suggest Eve or Frank as a friend for Alice, because
they are friends of Alice’s friends, but they are not already friends with Alice.
5.8.a Graph Traversal Algorithms
Graph traversal algorithms are used to visit all the nodes in a graph. There are two main traversal
algorithms: Breadth-First Search (BFS) and Depth-First Search (DFS).
1. Breadth-First Search (BFS): In BFS, we visit all the nodes in a graph in a breadth-first
manner, i.e., we visit all the nodes at a given distance from the source node before moving to
the nodes at the next distance. BFS uses a queue data structure to keep track of the nodes to
be visited. The algorithm is as follows:
◦ Enqueue the starting node in the queue.
◦ Mark the starting node as visited.
◦ While the queue is not empty:
(a) Dequeue a node from the queue.
(b) Visit the dequeued node.
(c) Enqueue all its adjacent nodes that have not been visited yet.
(d) Mark the adjacent nodes as visited.
2. Depth-First Search (DFS): In DFS, we visit all the nodes in a graph in a depth-first manner,
i.e., we visit all the nodes along a path from the source node before backtracking and visiting
other paths. DFS uses a stack data structure to keep track of the nodes to be visited. The
algorithm is as follows:
◦ Push the starting node onto the stack.
◦ Mark the starting node as visited.
◦ While the stack is not empty: a.
(a) Pop a node from the stack.
(b) Visit the popped node.
(c) Push all its unvisited adjacent nodes onto the stack.
(d) Mark the adjacent nodes as visited.
BFS and DFS can be used for different purposes. For example, BFS can be used to find the shortest
path between two nodes, while DFS can be used to find all the connected components in a graph.
References
1. Data Structures Using C, Reema Thareja, Oxford University Press India
2. Classic Data Structures, Samanta Debasis, Prentice Hall of India, 2nd ed.,