💻 C Programming
Learn C programming from the fundamentals to advanced concepts with examples, programs, explanations and practice.
📖 1. Introduction to C Programming
1.1 What is C?
C is a general-purpose, procedural programming language. It is one of the most important programming languages for learning programming fundamentals and problem solving.
C provides low-level memory access through pointers while also providing high-level programming features such as functions, loops and decision-making statements.
1.2 History of C
C was developed by Dennis Ritchie at Bell Labs in the early 1970s.
🔹 BCPL → B → C
🔹 C was developed at Bell Labs
🔹 C became widely used for system software
🔹 The UNIX operating system was largely developed using C
1.3 Features of C
- Simple: C has a relatively small set of core language features.
- Procedural: Programs can be organized into functions and procedures.
- Fast: C programs can execute efficiently.
- Portable: C programs can often be moved between different systems with limited changes.
- Structured: Large programs can be divided into smaller functions.
- Memory Access: Pointers provide direct access to memory addresses.
- Extensible: Programs can be organized using reusable functions and libraries.
1.4 Why Should You Learn C?
C is particularly useful for understanding how programs work internally.
✔ Variables and memory
✔ Arrays
✔ Functions
✔ Pointers
✔ Memory management
✔ Data Structures
✔ Algorithms
✔ Problem solving
1.5 Applications of C
C is still used in many areas of software and hardware development.
- Operating Systems
- Embedded Systems
- Device Drivers
- Compilers
- Networking Software
- Database Systems
- IoT Devices
- System Programming
1.6 C vs Other Programming Languages
| Feature | C | Python | Java |
|---|---|---|---|
| Type | Procedural | Multi-paradigm | Object-oriented |
| Execution | Compiled | Interpreted / bytecode-based | Compiled to bytecode |
| Memory Control | High | Mostly automatic | Mostly automatic |
| Learning Difficulty | Moderate | Beginner-friendly | Moderate |
1.7 Your First C Program
#include <stdio.h>
int main()
{
printf("Hello, World!");
return 0;
}
Output
1.8 Line-by-Line Explanation
Includes the Standard Input/Output library.
int main()
The main function is the entry point of a C program.
printf()
Used to display output on the screen.
return 0;
Indicates that the program finished successfully.
1.9 How Does a C Program Execute?
A C program is normally converted into executable machine code before it runs.
1.10 What is a Compiler?
A compiler translates source code written in a programming language into a form that the computer can execute.
C Source Code → C Compiler → Executable Program
1.11 Common Beginner Mistakes
- Forgetting the semicolon ;
- Missing braces { }
- Using incorrect format specifiers
- Forgetting & in appropriate scanf() calls
- Incorrect variable declarations
- Confusing = with ==
1.12 Quick Revision
📌 C is primarily a procedural, general-purpose programming language.
📌 main() is the entry point of a C program.
📌 printf() displays output.
📌 scanf() is commonly used for formatted input.
📌 Pointers allow programs to work with memory addresses.
📌 C is an important foundation for Data Structures and Algorithms.
1.13 Practice Questions
- Who developed the C programming language?
- What type of programming language is C?
- What is the purpose of main()?
- What is the purpose of printf()?
- What is a compiler?
- What is the purpose of #include?
- What is a pointer?
- Why is C called a structured language?
- Give three applications of C.
- Why is C important for learning Data Structures?
1.14 Beginner Programming Problems
Problem 2: Write a C program to add two numbers.
Problem 3: Write a C program to calculate the area of a circle.
Problem 4: Write a C program to check whether a number is positive or negative.
Problem 5: Write a C program to find the largest of two numbers.
1.15 Key Takeaway
Don't try to memorize C programs.
Instead, understand:
Input → Processing → Output
This thinking pattern will help you solve programming problems in C, Data Structures and eventually coding interviews.
🏗️ 2. Structure of a C Program
2.1 Basic Structure
A C program is generally organized into different parts. Each part has a specific purpose in the program.
#include <stdio.h>
int main()
{
// Variable declarations
// Input
// Processing
// Output
return 0;
}
2.2 Main Parts of a C Program
Used to include header files and perform preprocessing tasks.
2. main() Function
Execution of a C program begins from the main() function.
3. Variable Declarations
Variables are declared before they are used.
4. Input
Data can be received from the user using functions such as scanf().
5. Processing
The required calculations or logic are performed.
6. Output
Results can be displayed using printf().
7. return 0;
Indicates successful completion of the main function.
2.3 Header Files
Header files contain declarations and information needed by the program.
#include <stdio.h>
The header file stdio.h provides declarations for standard input and output functions such as printf() and scanf().
#include tells the
preprocessor to include the specified
header file.
2.4 main() Function
The main() function is the entry point of a hosted C program. Program execution begins from main().
int main()
{
printf("Hello");
return 0;
}
Indicates that main() returns an integer.
main
Name of the function from which execution begins.
()
Indicates that main is a function.
2.5 Curly Braces { }
Curly braces define a block of code. They indicate where a function, loop or conditional block begins and ends.
int main()
{
printf("Hello");
return 0;
}
The opening brace { starts the block and the closing brace } ends the block.
2.6 Semicolon ;
Most C statements end with a semicolon.
int age = 20;
printf("%d", age);
2.7 Comments
Comments are notes written inside the program for programmers. They are ignored by the compiler.
Single-line Comment
// This is a comment
int age = 20;
Multi-line Comment
/*
This is a
multi-line comment
*/
int age = 20;
2.8 Variable Declaration
A variable declaration specifies the data type and name of a variable.
int age;
float marks;
char grade;
A variable can also be declared and initialized at the same time.
int age = 20;
float marks = 85.5;
char grade = 'A';
2.9 Input Section
The scanf() function can be used to read formatted input from the user.
int age;
scanf("%d", &age);
2.10 Processing Section
Processing is the part of the program where calculations or logical operations are performed.
sum = a + b;
Here the values of a and b are added and the result is stored in sum.
2.11 Output Section
printf() is commonly used to display formatted output.
printf("%d", sum);
If sum contains 30, the output will be:
2.12 return 0;
return 0;
In the usual hosted C environment, returning 0 from main indicates successful program termination.
2.13 Complete Example
#include <stdio.h>
int main()
{
int a, b, sum;
printf("Enter two numbers: ");
scanf("%d %d", &a, &b);
sum = a + b;
printf("Sum = %d", sum);
return 0;
}
2.14 Sample Output
Sum = 30
2.15 Line-by-Line Explanation
| Code | Purpose |
|---|---|
| #include <stdio.h> | Includes standard input/output declarations. |
| int main() | Defines the main function. |
| int a, b, sum; | Declares three integer variables. |
| printf() | Displays a message. |
| scanf() | Reads values entered by the user. |
| sum = a + b; | Adds the two numbers. |
| printf("Sum = %d", sum); | Displays the calculated result. |
| return 0; | Indicates successful completion. |
2.16 Input → Processing → Output
Most beginner programming problems can be understood using the IPO model.
Values entered by the user
↓
PROCESSING
Calculations or logic
↓
OUTPUT
Final result
Example
Input: a = 10, b = 20
Processing: sum = a + b
Output: 30
2.17 Program Execution Flow
↓
Preprocessor
↓
Compiler
↓
Object Code
↓
Linker
↓
Executable Program
↓
Program Execution
2.18 Common Mistakes
- Forgetting ;
- Forgetting closing brace }
- Writing Printf instead of printf
- Writing Scanf instead of scanf
- Forgetting & in appropriate scanf() calls
- Using a variable before declaring it
- Forgetting to include stdio.h when using printf() or scanf()
2.19 Quick Revision
📌 stdio.h provides declarations for standard input/output functions.
📌 main() is the entry point of a hosted C program.
📌 Statements generally end with ;.
📌 Curly braces { } define blocks of code.
📌 printf() is commonly used for output.
📌 scanf() is commonly used for formatted input.
📌 Most beginner programs can be understood using Input → Processing → Output.
2.20 Practice Questions
- What is the purpose of #include?
- What is the role of stdio.h?
- What is the purpose of main()?
- Why are curly braces used?
- Why is a semicolon used in C?
- What are comments?
- What is the purpose of scanf()?
- What is the purpose of printf()?
- What does return 0 mean when returned from main()?
- Explain the Input → Processing → Output model with an example.
2.21 Programming Problems
Write a C program to add two numbers.
Problem 2
Write a C program to calculate the average of three numbers.
Problem 3
Write a C program to calculate the area of a rectangle.
Problem 4
Write a C program to calculate the simple interest.
Problem 5
Write a C program to convert temperature from Celsius to Fahrenheit.
2.22 Key Takeaway
1. What is the INPUT?
2. What PROCESSING is required?
3. What should be the OUTPUT?
This simple approach will become the foundation for solving more difficult programming problems.
🔤 3. Variables & Constants
3.1 What is a Variable?
A variable is a named object that represents a storage location used by a C program. Its value can generally be changed during program execution.
Every variable has a type, a name and, when initialized, an initial value.
int age = 25;
int → Data type
age → Variable name
25 → Initial value
3.2 Why Do We Need Variables?
Variables allow programs to store data and use that data in calculations and decision making.
int age = 20;
int nextYear = age + 1;
printf("%d", nextYear);
3.3 Variable Declaration
Declaration tells the compiler about the variable's type and name.
int age;
float marks;
char grade;
In these declarations, memory/storage requirements are determined by the type and implementation.
3.4 Variable Initialization
Initialization means giving a variable an initial value when it is defined.
int age = 20;
float marks = 85.5;
char grade = 'A';
3.5 Declaration vs Initialization
| Concept | Example | Meaning |
|---|---|---|
| Declaration | int age; | Introduces a variable with a type and name. |
| Initialization | int age = 20; | Gives an initial value when the variable is defined. |
| Assignment | age = 25; | Assigns a value to an already declared variable. |
3.6 Assigning Values
The assignment operator = is used to assign a value to a variable.
int age;
age = 20;
age = 25;
After the second assignment, the value of age is 25.
= means assignment.
== means equality comparison.
3.7 Rules for Naming Variables
Variable names are identifiers and must follow the rules of the C language.
- A name may contain letters, digits and underscores.
- A name must not begin with a digit.
- Spaces are not allowed.
- C is case-sensitive.
- Keywords cannot be used as variable names.
- Choose meaningful names whenever possible.
3.8 Valid and Invalid Identifiers
| Identifier | Valid? | Reason |
|---|---|---|
| age | ✔ Yes | Valid identifier |
| student_name | ✔ Yes | Underscore is allowed |
| marks2 | ✔ Yes | Digits can appear after the first character |
| 2marks | ❌ No | Cannot begin with a digit |
| student name | ❌ No | Spaces are not allowed |
| float | ❌ No | float is a C keyword |
| total-marks | ❌ No | Hyphen is not allowed in an identifier |
3.9 C is Case-Sensitive
C treats uppercase and lowercase letters as different characters.
int age = 20;
int Age = 30;
int AGE = 40;
These are three different identifiers.
3.10 What is a Constant?
A constant is a value that is intended not to change during the relevant part of a program.
10
3.14
'A'
"Hello"
3.11 const Keyword
The const qualifier can be used to make an object read-only through that particular identifier.
const int MAX = 100;
After initialization, you should not assign a new value to MAX through that identifier.
const int MAX = 100;
/* MAX = 200; invalid modification */
3.12 #define Constants
The preprocessor directive #define can be used to create a macro.
#define PI 3.14159
int main()
{
printf("%f", PI);
return 0;
}
The preprocessor replaces occurrences of the macro name according to the macro definition before compilation.
3.13 Variables and Memory
A variable is associated with a storage location in memory. The exact address and size depend on the type and the implementation.
int age = 25;
┌─────────────────────┐
│ name : age │
├─────────────────────┤
│ value : 25 │
└─────────────────────┘
Later, when we learn pointers, we will see how a program can work with the address of such an object.
3.14 Multiple Variables
Multiple variables of the same type can be declared in one declaration.
int a, b, c;
They can also be initialized together.
int a = 10, b = 20, c = 30;
3.15 Complete Example
#include <stdio.h>
int main()
{
int age = 20;
float marks = 85.5;
char grade = 'A';
printf("Age = %d\n", age);
printf("Marks = %.2f\n", marks);
printf("Grade = %c\n", grade);
return 0;
}
Output
Marks = 85.50
Grade = A
3.16 Common Mistakes
- Using a variable before defining it when a definition is required.
- Using a keyword as a variable name.
- Starting a variable name with a digit.
- Using spaces in variable names.
- Confusing = with ==.
- Trying to modify an object declared with const.
- Using unclear variable names in larger programs.
3.17 Quick Revision
📌 Declaration introduces a variable with its type and name.
📌 Initialization gives an initial value when an object is defined.
📌 Assignment changes the stored value when the object is modifiable.
📌 C identifiers are case-sensitive.
📌 Keywords cannot be used as identifiers.
📌 const can make an object read-only through that identifier.
📌 #define creates a preprocessor macro.
3.18 Quick MCQs
-
Which symbol is used for assignment?
A) ==
B) =
C) !=
D) >
Answer: B -
Which is a valid identifier?
A) 2value
B) student name
C) student_name
D) float
Answer: C -
Which keyword can be used to qualify
an object as read-only?
A) fixed
B) constant
C) const
D) readonly
Answer: C -
Which of the following is case-sensitive
in C?
A) Identifiers
B) Spaces
C) Comments
D) Output
Answer: A -
Which is a valid declaration?
A) int 2age;
B) int age;
C) integer age;
D) number age;
Answer: B
3.19 Programming Problems
Declare variables to store a student's age, marks and grade and print them.
Problem 2
Write a C program to exchange the values of two variables using a third variable.
Problem 3
Write a C program to calculate the total and average of three marks.
Problem 4
Create a constant for PI and calculate the area of a circle.
Problem 5
Write a program to calculate the total price of three products.
3.20 Key Takeaway
What data do I need?
What type should each data item have?
Which values can change?
Which values should remain unchanged?
Choosing appropriate variables and constants is one of the first steps toward writing clear and reliable programs.
📦 4. Data Types
4.1 What is a Data Type?
A data type tells the compiler what kind of value an object can represent and helps determine how that object is stored and interpreted.
Choosing the correct data type is important because different types are designed for different kinds of data.
int → Whole numbers
float → Decimal numbers
double → Double-precision floating-point values
char → Character values
4.2 Main Categories of C Data Types
├── Basic / Fundamental Types
│ ├── char
│ ├── int
│ ├── float
│ └── double
├── void
├── Derived Types
│ ├── Arrays
│ ├── Pointers
│ └── Functions
└── User-defined Types
├── struct
├── union
└── enum
4.3 Basic Data Types
| Type | Used For | Example |
|---|---|---|
| char | Character values | 'A' |
| int | Integer values | 25 |
| float | Single-precision floating-point values | 25.5f |
| double | Double-precision floating-point values | 25.5678 |
| void | Represents absence of a value | void function |
4.4 char Data Type
The char type is used to store a character value.
char grade = 'A';
printf("%c", grade);
Character constants are written using single quotes.
'A'
'B'
'7'
'@'
4.5 int Data Type
The int type is commonly used to represent integer values.
int age = 25;
int marks = 90;
printf("%d", age);
Integer values do not contain a fractional part.
10
-25
0
1000
4.6 float Data Type
The float type represents single-precision floating-point values.
float temperature = 36.5f;
printf("%.2f", temperature);
The suffix f can be used to indicate a floating constant of type float.
4.7 double Data Type
The double type provides double-precision floating-point values and is commonly preferred when more precision than float is useful.
double pi = 3.141592653589793;
printf("%.15f", pi);
4.8 void Data Type
The void type represents the absence of a value in certain contexts.
Example: Function with no return value
void display()
{
printf("Hello");
}
A function declared with void return type does not return a value to its caller.
4.9 Type Modifiers
C provides type specifiers and modifiers that can be combined with integer types to change their range and representation.
signed
unsigned
short
long
4.10 signed and unsigned
Integer types can be signed or unsigned. A signed type can represent negative and non-negative values, while an unsigned type represents only non-negative values.
signed int temperature = -10;
unsigned int count = 100;
4.11 short and long
The keywords short and long can be used to request different integer ranges.
short int a;
long int b;
long long int c;
The exact size of integer types is implementation-dependent, so portable programs should not assume a particular byte size unless the implementation guarantees it.
4.12 sizeof() Operator
The sizeof operator gives the size in bytes of a type or object.
#include <stdio.h>
int main()
{
printf("%zu\n", sizeof(char));
printf("%zu\n", sizeof(int));
printf("%zu\n", sizeof(float));
printf("%zu\n", sizeof(double));
return 0;
}
The result can vary between systems, especially for some integer types. Use sizeof() when your program needs the actual size on the current implementation.
4.13 Common Format Specifiers
| Data | Common printf() Specifier | Example |
|---|---|---|
| int | %d | printf("%d", age); |
| unsigned int | %u | printf("%u", count); |
| char | %c | printf("%c", grade); |
| float | %f | printf("%f", value); |
| double | %f | printf("%f", value); |
| string | %s | printf("%s", name); |
4.14 Character vs Integer
A character constant such as 'A' and an integer constant such as 65 are different expressions, even though a character can be represented by an integer value in appropriate contexts.
char ch = 'A';
int number = 65;
printf("%c\n", ch);
printf("%d\n", number);
The numerical value associated with a character depends on the execution character set used by the implementation.
4.15 float vs double
| Feature | float | double |
|---|---|---|
| Precision | Single precision | Double precision |
| Typical Use | When lower precision or lower storage is appropriate | When greater precision is needed |
| Common Literal | 3.14f | 3.14 |
4.16 Type Conversion
Type conversion happens when a value of one type is converted to another type.
Implicit Conversion
The compiler may automatically convert one type to another according to the rules of the C language.
int a = 10;
double b = a;
printf("%f", b);
Explicit Conversion
A programmer can explicitly request a conversion using a cast.
int a = 5;
int b = 2;
double result = (double)a / b;
printf("%f", result);
4.17 Important: Integer Division
When both operands of division are integers, integer division is performed.
int a = 5;
int b = 2;
printf("%d", a / b);
If a fractional result is required, convert at least one operand to a floating-point type.
printf("%f", (double)a / b);
4.18 Complete Example
#include <stdio.h>
int main()
{
int age = 20;
float marks = 85.5f;
double pi = 3.141592653589793;
char grade = 'A';
printf("Age = %d\n", age);
printf("Marks = %.2f\n", marks);
printf("Pi = %.15f\n", pi);
printf("Grade = %c\n", grade);
printf("Size of int = %zu bytes\n",
sizeof(int));
return 0;
}
4.19 Common Mistakes
- Assuming every int is exactly 4 bytes on every C implementation.
- Confusing 'A' with "A".
- Forgetting the f suffix when you specifically want a float literal such as 3.14f.
- Expecting integer division to produce a fractional result.
- Using the wrong format specifier.
- Assuming float and double have the same precision.
- Assuming data type sizes are identical on every compiler and platform.
4.20 Quick Revision
📌 int is commonly used for integer values.
📌 float represents single-precision floating-point values.
📌 double represents double-precision floating-point values.
📌 void represents the absence of a value in certain contexts.
📌 sizeof reports size in bytes.
📌 Use sizeof() when you need the actual size on your system.
📌 Integer division discards the fractional part of the result.
4.21 Quick MCQs
-
Which data type is commonly used
to store an integer?
A) float
B) int
C) char
D) void
Answer: B -
Which operator gives the size of
a type or object?
A) size
B) length
C) sizeof
D) bytes
Answer: C -
Which is a character constant?
A) "A"
B) 'A'
C) A
D) `A`
Answer: B -
What is the result of 5 / 2 when
both operands are int?
A) 2.5
B) 3
C) 2
D) 0
Answer: C -
Which type generally provides more
precision than float?
A) char
B) int
C) double
D) void
Answer: C
4.22 Programming Problems
Declare variables of type int, float, double and char and print their values.
Problem 2
Write a program to print the size of char, int, float and double using sizeof.
Problem 3
Write a program to calculate the average of two integers and display the result with decimal precision.
Problem 4
Write a program demonstrating the difference between integer division and floating-point division.
Problem 5
Write a program that reads an integer and displays its value as a double.
4.23 Key Takeaway
Understand three things:
1. What kind of value do I need?
2. What type is appropriate?
3. What happens when different types are used together?
These ideas become extremely important when we learn operators, expressions, arrays, pointers and functions.
⌨️ 5. Input & Output
5.1 What is Input and Output?
A program often needs to receive data from the user, process that data and display the result.
↓
PROCESSING
↓
OUTPUT
Input → 10 and 20
Processing → 10 + 20
Output → 30
5.2 printf() Function
The printf() function is commonly used to display formatted output on the standard output stream.
#include <stdio.h>
int main()
{
printf("Hello World");
return 0;
}
5.3 Printing Text
Text written inside double quotation marks is printed as a string.
printf("Welcome to C Programming");
5.4 Printing Variables
Format specifiers are used to display values stored in variables.
int age = 25;
printf("%d", age);
5.5 Common Format Specifiers
| Specifier | Common Use | Example |
|---|---|---|
| %d | int | printf("%d", age); |
| %u | unsigned int | printf("%u", count); |
| %c | char | printf("%c", grade); |
| %f | floating-point output | printf("%f", value); |
| %s | string | printf("%s", name); |
| %zu | sizeof result | printf("%zu", sizeof(int)); |
5.6 Printing Integers
int a = 100;
printf("%d", a);
5.7 Printing Characters
char grade = 'A';
printf("%c", grade);
'A'
Strings use double quotes:
"A"
5.8 Printing Floating-Point Values
float marks = 85.5f;
printf("%f", marks);
5.9 Controlling Decimal Places
A precision such as %.2f can be used to display two digits after the decimal point.
float marks = 85.5678f;
printf("%.2f", marks);
Similarly:
printf("%.1f", marks);
printf("%.3f", marks);
5.10 Escape Sequences
Escape sequences represent special characters inside strings and character constants.
| Escape Sequence | Meaning |
|---|---|
| \n | New line |
| \t | Horizontal tab |
| \\ | Backslash |
| \" | Double quotation mark |
| \' | Single quotation mark |
Example
printf("Hello\nWorld");
World
5.11 scanf() Function
The scanf() function is commonly used to read formatted input from the standard input stream.
int age;
scanf("%d", &age);
5.12 Why Do We Use & in scanf()?
For most ordinary scalar variables, scanf() needs the address of the object where the input value should be stored.
int age;
scanf("%d", &age);
↓
Memory location
&age
↓
Address of age
scanf("%d", &age);
The & is important.
5.13 Reading Multiple Values
int a, b;
scanf("%d %d", &a, &b);
If the input is:
then:
b = 20
5.14 Reading Different Data Types
int age;
float marks;
char grade;
scanf("%d", &age);
scanf("%f", &marks);
scanf(" %c", &grade);
scanf(" %c", &grade);
This can help skip leading whitespace such as a leftover newline.
5.15 Reading a double
For scanf(), the conversion specifier for a double is %lf.
double price;
scanf("%lf", &price);
printf() → double → %f
scanf() → double → %lf
5.16 Reading a Character
char ch;
scanf(" %c", &ch);
printf("%c", ch);
The leading space in the format string tells scanf() to skip leading whitespace before reading the character.
5.17 Reading a String
A character array can be used to store a string. When using scanf() with %s, the array name is passed without &.
char name[30];
scanf("%29s", name);
printf("%s", name);
The width limit helps prevent writing more characters than the array can hold.
For example, entering:
Ravi Kumar
reads only Ravi with a normal %s conversion.
5.18 printf() vs scanf()
| Feature | printf() | scanf() |
|---|---|---|
| Purpose | Output | Input |
| Common int specifier | %d | %d |
| Common char specifier | %c | %c |
| double | %f | %lf |
| Normal int variable | age | &age |
5.19 Complete Example
#include <stdio.h>
int main()
{
int age;
float marks;
printf("Enter your age: ");
scanf("%d", &age);
printf("Enter your marks: ");
scanf("%f", &marks);
printf("\nAge = %d\n", age);
printf("Marks = %.2f\n", marks);
return 0;
}
Sample Input
85.5
Sample Output
Marks = 85.50
5.20 Multiple Input Example
#include <stdio.h>
int main()
{
int a, b;
scanf("%d %d", &a, &b);
printf("Sum = %d", a + b);
return 0;
}
Input
Output
5.21 Common Input/Output Mistakes
- Forgetting & for ordinary scalar variables in scanf().
- Using the wrong format specifier.
- Using %f instead of %lf for a double argument in scanf().
- Using %d for a float or double input.
- Forgetting the newline escape sequence when separate output lines are needed.
- Using %s when the input can contain spaces.
- Forgetting the width limit when reading a string with scanf().
- Forgetting the leading space in a character input format when whitespace needs to be skipped.
5.22 Quick Revision
📌 scanf() is commonly used for formatted input.
📌 %d → int
📌 %u → unsigned int
📌 %c → char
📌 %f → floating-point output
📌 %lf → double input with scanf()
📌 %s → string
📌 \n → new line
📌 &variable gives the address of a variable.
5.23 Quick MCQs
-
Which function is commonly used to
display formatted output?
A) scanf()
B) printf()
C) input()
D) read()
Answer: B -
Which function is commonly used to
read formatted input?
A) printf()
B) display()
C) scanf()
D) output()
Answer: C -
Which format specifier is commonly
used for an int?
A) %f
B) %c
C) %d
D) %s
Answer: C -
Which specifier is used to read a
double using scanf()?
A) %d
B) %f
C) %lf
D) %c
Answer: C -
Which escape sequence moves the cursor
to the next line?
A) \t
B) \n
C) \\
D) \a
Answer: B
5.24 Programming Problems
Read two integers and print their sum.
Problem 2
Read three integers and print their average with decimal precision.
Problem 3
Read a student's name, age and marks and display them.
Problem 4
Read the radius of a circle and print its area up to two decimal places.
Problem 5
Read two numbers and print their sum, difference, product and quotient.
Problem 6
Read a character and print the character entered by the user.
5.25 Key Takeaway
1. Identify the input data.
2. Choose the correct data type.
3. Use the correct scanf() format.
4. Process the data.
5. Use the correct printf() format.
This pattern is the foundation of almost every beginner coding-platform problem.
➕ 6. Operators
6.1 What is an Operator?
An operator is a symbol that tells the compiler to perform an operation on one or more operands.
a + b
+ → Operator
a, b → Operands
6.2 Types of Operators in C
├── Arithmetic
├── Relational
├── Logical
├── Assignment
├── Increment / Decrement
├── Conditional
├── Bitwise
└── Other / Special Operators
6.3 Arithmetic Operators
Arithmetic operators are used to perform mathematical calculations.
| Operator | Meaning | Example |
|---|---|---|
| + | Addition | a + b |
| - | Subtraction | a - b |
| * | Multiplication | a * b |
| / | Division | a / b |
| % | Remainder | a % b |
6.4 Arithmetic Example
int a = 10;
int b = 3;
printf("%d\n", a + b);
printf("%d\n", a - b);
printf("%d\n", a * b);
printf("%d\n", a / b);
printf("%d\n", a % b);
7
30
3
1
6.5 Modulus Operator %
The modulus operator gives the remainder of an integer division.
10 % 3
Quotient = 3
Remainder = 1
Therefore:
10 % 3 = 1
Common Uses
- Checking whether a number is even or odd.
- Finding remainders.
- Working with repeating patterns.
- Extracting digits from integers.
int n = 25;
if (n % 2 == 0)
{
printf("Even");
}
else
{
printf("Odd");
}
6.6 Integer Division
When both operands are integers, division produces an integer result.
int a = 5;
int b = 2;
printf("%d", a / b);
If you need a fractional result, use a floating-point operand.
printf("%f", (double)a / b);
6.7 Relational Operators
Relational operators compare two values. The result is an integer value of 0 or 1 in the usual conditional-expression context: 0 means false and nonzero means true.
| Operator | Meaning |
|---|---|
| > | Greater than |
| < | Less than |
| >= | Greater than or equal to |
| <= | Less than or equal to |
| == | Equal to |
| != | Not equal to |
Example
int a = 10;
int b = 20;
printf("%d\n", a > b);
printf("%d\n", a < b);
printf("%d\n", a == b);
1
0
6.8 Assignment Operators
Assignment operators store a value in a modifiable object.
| Operator | Meaning | Equivalent Form |
|---|---|---|
| = | Assignment | a = b |
| += | Add and assign | a = a + b |
| -= | Subtract and assign | a = a - b |
| *= | Multiply and assign | a = a * b |
| /= | Divide and assign | a = a / b |
| %= | Modulus and assign | a = a % b |
Example
int a = 10;
a += 5;
printf("%d", a);
6.9 Logical Operators
Logical operators are commonly used to combine or negate conditions.
| Operator | Meaning |
|---|---|
| && | Logical AND |
| || | Logical OR |
| ! | Logical NOT |
6.10 Logical AND &&
AND is true only when both conditions evaluate to true.
int age = 20;
if (age >= 18 && age <= 60)
{
printf("Eligible");
}
TRUE && FALSE → FALSE
FALSE && TRUE → FALSE
FALSE && FALSE → FALSE
6.11 Logical OR ||
OR is true when at least one condition evaluates to true.
TRUE || FALSE → TRUE
FALSE || TRUE → TRUE
FALSE || FALSE → FALSE
6.12 Logical NOT !
NOT reverses the logical truth value of its operand.
int x = 0;
if (!x)
{
printf("Condition is true");
}
6.13 Short-Circuit Evaluation
C evaluates logical AND and OR from left to right and may skip the remaining operands when the result is already known.
AND Example
if (x != 0 && 10 / x > 2)
{
printf("Valid");
}
If x is 0, the second condition is not evaluated because the left side is already false.
OR Example
if (x == 0 || y > 10)
{
printf("Condition satisfied");
}
6.14 Increment Operator ++
The increment operator increases a modifiable integer object by one.
int a = 5;
a++;
printf("%d", a);
6.15 Decrement Operator --
The decrement operator decreases a modifiable integer object by one.
int a = 5;
a--;
printf("%d", a);
6.16 Pre-Increment vs Post-Increment
Pre-Increment
int a = 5;
int b = ++a;
a = 6
Then assign 6 to b
a = 6
b = 6
Post-Increment
int a = 5;
int b = a++;
b = 5
Then increase a
a = 6
++a → Change first, use later
a++ → Use first, change later
6.17 Conditional Operator ?:
The conditional operator is a compact expression for choosing between two values.
int a = 10;
int b = 20;
int max = (a > b) ? a : b;
printf("%d", max);
6.18 Bitwise Operators
Bitwise operators operate on the individual bits of integer operands.
| Operator | Meaning |
|---|---|
| & | Bitwise AND |
| | | Bitwise OR |
| ^ | Bitwise XOR |
| ~ | Bitwise NOT |
| << | Left shift |
| >> | Right shift |
6.19 Bitwise Example
Consider:
int a = 5;
int b = 3;
In binary representation:
3 → 0011
printf("%d", a & b);
Bitwise AND compares corresponding bits.
6.20 Bitwise XOR
XOR produces 1 when the corresponding bits are different.
int a = 5;
int b = 3;
printf("%d", a ^ b);
3 → 0011
XOR
0110 → 6
6.21 Shift Operators
Left Shift
int x = 5;
printf("%d", x << 1);
Right Shift
int x = 8;
printf("%d", x >> 1);
The exact behavior of shifts can depend on the signedness and value of the operand, especially for negative values. The simple examples above use positive integers.
6.22 Operator Summary
| Category | Operators | Purpose |
|---|---|---|
| Arithmetic | + - * / % | Mathematical operations |
| Relational | > < >= <= == != | Compare values |
| Logical | && || ! | Combine or negate conditions |
| Assignment | = += -= *= /= %= | Assign values |
| Increment / Decrement | ++ -- | Increase or decrease by one |
| Conditional | ?: | Select one of two expressions |
| Bitwise | & | ^ ~ << >> | Operate on integer bits |
6.23 Operator Precedence
When an expression contains multiple operators, precedence determines which operators are grouped first.
int result = 10 + 5 * 2;
Multiplication has higher precedence than addition.
= 10 + 10
= 20
Use Parentheses for Clarity
int result = (10 + 5) * 2;
6.24 Associativity
When operators of the same precedence occur together, associativity determines how they are grouped.
For example, subtraction is generally left-associative.
20 - 5 - 3
= 12
6.25 Common Mistakes
- Confusing = with ==.
- Forgetting that integer division discards the fractional part.
- Confusing % with percentage.
- Confusing && with bitwise &.
- Confusing || with bitwise |.
- Confusing ++a with a++.
- Writing complicated expressions without parentheses.
- Assuming bitwise operations are the same as logical operations.
6.26 Quick Revision
📌 - → Subtraction
📌 * → Multiplication
📌 / → Division
📌 % → Remainder
📌 == → Equality comparison
📌 = → Assignment
📌 && → Logical AND
📌 || → Logical OR
📌 ! → Logical NOT
📌 ++ → Increment
📌 -- → Decrement
📌 ?: → Conditional operator
📌 & | ^ ~ << >> → Bitwise operators
6.27 Quick MCQs
-
What is the result of 10 % 3?
A) 0
B) 1
C) 3
D) 10
Answer: B -
Which operator is used for equality
comparison?
A) =
B) ==
C) !=
D) >=
Answer: B -
What is the result of 5 / 2 when
both operands are integers?
A) 2.5
B) 3
C) 2
D) 1
Answer: C -
Which operator means logical AND?
A) &
B) &&
C) |
D) ||
Answer: B -
What is the value of a after:
int a = 5;
++a;
A) 4
B) 5
C) 6
D) 7
Answer: C -
Which operator is used for bitwise XOR?
A) &
B) |
C) ^
D) ~
Answer: C
6.28 Programming Problems
Read two integers and print their sum, difference, product, quotient and remainder.
Problem 2
Check whether a given integer is even or odd using the modulus operator.
Problem 3
Read three numbers and find the largest using relational and logical operators.
Problem 4
Demonstrate the difference between pre-increment and post-increment.
Problem 5
Check whether a number lies between 10 and 100 using logical operators.
Problem 6
Find the larger of two numbers using the conditional operator.
Problem 7
Perform AND, OR and XOR operations on two integers.
Problem 8
Demonstrate left-shift and right-shift operations using positive integers.
6.29 Key Takeaway
Before solving a coding problem, identify:
What calculation is required?
What comparison is required?
Are multiple conditions involved?
Is integer or floating-point arithmetic required?
Once you understand operators well, decision-making and loops become much easier.
🧮 7. Expressions
7.1 What is an Expression?
An expression is a combination of constants, variables, operators and function calls that represents a value.
a + b
Here:
a and b → Operands
+ → Operator
a + b → Expression
7.2 Simple Expressions
int a = 10;
int b = 20;
int result = a + b;
The expression a + b produces the value 30.
7.3 Types of Expressions
| Type | Example | Purpose |
|---|---|---|
| Arithmetic | a + b | Mathematical calculation |
| Relational | a > b | Comparison |
| Logical | a > 0 && b > 0 | Combine conditions |
| Assignment | a = 10 | Assign a value |
| Conditional | a > b ? a : b | Select a value |
7.4 Arithmetic Expressions
int a = 10;
int b = 3;
int x = a + b;
int y = a - b;
int z = a * b;
int p = a / b;
int q = a % b;
a - b = 7
a * b = 30
a / b = 3
a % b = 1
7.5 Relational Expressions
A relational expression compares two values.
int a = 10;
int b = 20;
printf("%d", a < b);
The expression a < b is true, so the result is nonzero; for this simple comparison it is 1.
7.6 Logical Expressions
int age = 25;
int result = age >= 18 && age <= 60;
printf("%d", result);
Both conditions are true, so the logical AND expression evaluates to true.
7.7 Assignment Expressions
int a;
a = 10;
The expression a = 10 assigns the value 10 to a.
7.8 Mixed Expressions
An expression may contain several different operators.
int result = 10 + 5 * 2;
Multiplication has higher precedence than addition.
= 10 + 10
= 20
7.9 Operator Precedence
Operator precedence determines which operators are grouped first when an expression contains multiple operators.
Important Precedence Order
| Priority | Operators | Category |
|---|---|---|
| 1 | () | Parentheses / Function call |
| 2 | ++ -- + - ! ~ | Unary operators |
| 3 | * / % | Multiplication, division, remainder |
| 4 | + - | Addition, subtraction |
| 5 | << >> | Shift |
| 6 | < <= > >= | Relational |
| 7 | == != | Equality |
| 8 | & | Bitwise AND |
| 9 | ^ | Bitwise XOR |
| 10 | | | Bitwise OR |
| 11 | && | Logical AND |
| 12 | || | Logical OR |
| 13 | ?: | Conditional |
| 14 | = += -= *= /= %= | Assignment |
7.10 Precedence Example 1
int result = 10 + 5 * 2;
5 × 2 = 10
Step 2:
10 + 10 = 20
7.11 Parentheses Change the Result
int result = (10 + 5) * 2;
(10 + 5) = 15
Step 2:
15 × 2 = 30
7.12 Associativity
Associativity determines how operators with the same precedence are grouped.
Left-to-Right Example
int result = 20 - 5 - 3;
= 15 - 3
= 12
Multiplication and Division
int result = 20 / 5 * 2;
= 4 × 2
= 8
7.13 Integer Expression
int a = 5;
int b = 2;
int result = a / b;
Both operands are integers, so integer division is performed.
7.14 Floating-Point Expression
int a = 5;
int b = 2;
float result = (float)a / b;
printf("%.2f", result);
float result = a / b;
performs integer division first because both operands are int.
float result = (float)a / b;
converts one operand to float before division.
7.15 Type Conversion
Type conversion occurs when a value is converted from one data type to another.
Implicit Conversion
The compiler performs the conversion automatically when appropriate.
int a = 10;
double b = 2.5;
double result = a + b;
Here, a is converted to a compatible floating-point type for the calculation.
Explicit Conversion
The programmer explicitly requests a conversion using a cast.
int a = 5;
int b = 2;
double result = (double)a / b;
7.16 Type Casting
A cast has the form:
Example
int marks = 85;
int total = 100;
float percentage =
(float)marks / total * 100;
printf("%.2f", percentage);
7.17 Character Expressions
In C, a character constant such as 'A' has an integer value associated with its character code.
char ch = 'A';
printf("%d", ch);
The exact numeric value depends on the execution character set. On systems using ASCII, 'A' is 65.
7.18 Truth Values in C
In conditions, zero represents false and any nonzero value represents true.
int a = 10;
if (a)
{
printf("True");
}
int a = 0;
if (a)
{
printf("True");
}
else
{
printf("False");
}
7.19 Logical Expression Evaluation
int a = 10;
int b = 20;
int result = (a < b) && (b > 15);
b > 15 → TRUE
TRUE && TRUE
= TRUE
7.20 Conditional Expression
int a = 10;
int b = 20;
int max = (a > b) ? a : b;
FALSE
Therefore choose b
max = 20
7.21 Step-by-Step Expression Evaluation
int result = 10 + 20 / 5 * 2 - 3;
Evaluate according to precedence and associativity.
4 × 2 = 8
10 + 8 - 3
18 - 3
Result = 15
7.22 Tricky Expression Example
int a = 5;
int b = 10;
int result = a + b * 2;
= 20
a + 20
= 25
7.23 Increment Expressions
Pre-Increment
int a = 5;
int b = ++a;
a = 6
Then:
b = 6
Final:
a = 6, b = 6
Post-Increment
int a = 5;
int b = a++;
b = 5
Then:
a = 6
Final:
a = 6, b = 5
7.24 ⚠️ Avoid Dangerous Expressions
Do not write complicated expressions that modify the same scalar object multiple times when the evaluation order is not clearly defined.
For example, avoid expressions such as:
i++ + ++i
Prefer separate statements:
i++;
i++;
Clear code is safer code.
7.25 Common Mistakes
- Forgetting operator precedence.
- Assuming all operators are evaluated from left to right.
- Confusing integer division with floating-point division.
- Forgetting to cast before division.
- Confusing = with ==.
- Confusing & with &&.
- Confusing | with ||.
- Writing overly complicated expressions.
7.26 Quick Revision
📌 Parentheses can explicitly control grouping.
📌 * / % generally have higher precedence than + -.
📌 Operators with equal precedence follow their specified associativity.
📌 Integer ÷ Integer → Integer division.
📌 Cast one operand when floating-point division is required.
📌 Zero → false.
📌 Nonzero → true.
7.27 Quick MCQs
-
What is the result of:
10 + 5 * 2
A) 30
B) 20
C) 25
D) 15
Answer: B -
What is the result of:
5 / 2
when both operands are int?
A) 2.5
B) 3
C) 2
D) 1
Answer: C -
Which operator has higher precedence?
A) +
B) *
C) =
D) ||
Answer: B -
What is the result of:
(10 + 5) * 2
A) 20
B) 25
C) 30
D) 15
Answer: C -
What is the value of b?
int a = 5;
int b = a++;
A) 4
B) 5
C) 6
D) Undefined
Answer: B -
What is the result of:
(double)5 / 2
A) 2
B) 2.0
C) 2.5
D) 3
Answer: C
7.28 Practice Problems
Evaluate:
10 + 5 * 2
Problem 2
Evaluate:
(10 + 5) * 2
Problem 3
Evaluate:
20 / 5 * 2
Problem 4
Read two integers and calculate their average as a floating-point value.
Problem 5
Find the percentage of marks obtained using explicit type casting.
Problem 6
Predict the values of a and b:
int a = 5;
int b = ++a;
Problem 7
Predict the values of a and b:
int a = 5;
int b = a++;
Problem 8
Find the largest of two numbers using a conditional expression.
Problem 9
Evaluate:
10 + 20 / 5 * 2 - 3
Problem 10
Write a program to demonstrate the difference between integer division and floating-point division.
7.29 Key Takeaway
Step 1: Check parentheses.
Step 2: Identify operator precedence.
Step 3: Apply associativity where necessary.
Step 4: Check data types.
Step 5: Check whether integer or floating-point arithmetic is being performed.
Step 6: Evaluate the expression step by step.
This method is extremely useful for C programming exams and coding interviews.
🔀 8. Decision Making
8.1 What is Decision Making?
Decision making allows a program to choose different actions depending on whether a condition is true or false.
↓
Condition
↓
Decision
↓
Output
If it is raining → Take an umbrella.
Otherwise → Do not take an umbrella.
A C program can express the same idea using an if-else statement.
8.2 Decision-Making Statements
| Statement | Purpose |
|---|---|
| if | Execute code when a condition is true |
| if-else | Choose between two alternatives |
| else-if ladder | Choose among multiple conditions |
| Nested if | Place one decision inside another |
| switch | Select among multiple constant cases |
8.3 if Statement
The if statement executes a block of code only when its condition evaluates to true.
{
statements;
}
Example
int age = 20;
if (age >= 18)
{
printf("Eligible");
}
How it works
↓
age >= 18 ?
↓
TRUE
↓
Print "Eligible"
8.4 if-else Statement
The if-else statement provides two possible paths.
{
statement 1;
}
else
{
statement 2;
}
Example: Pass or Fail
int marks = 35;
if (marks >= 40)
{
printf("Pass");
}
else
{
printf("Fail");
}
8.5 Example: Even or Odd
int n;
scanf("%d", &n);
if (n % 2 == 0)
{
printf("Even");
}
else
{
printf("Odd");
}
If n % 2 == 0
→ Even
Otherwise
→ Odd
8.6 Example: Positive, Negative or Zero
int n;
scanf("%d", &n);
if (n > 0)
{
printf("Positive");
}
else if (n < 0)
{
printf("Negative");
}
else
{
printf("Zero");
}
8.7 else-if Ladder
An else-if ladder is used when there are multiple possible conditions.
↓
else if (condition2)
↓
else if (condition3)
↓
else
Example: Grade Calculation
int marks;
scanf("%d", &marks);
if (marks >= 90)
{
printf("Grade A");
}
else if (marks >= 75)
{
printf("Grade B");
}
else if (marks >= 60)
{
printf("Grade C");
}
else if (marks >= 40)
{
printf("Grade D");
}
else
{
printf("Fail");
}
| Marks | Grade |
|---|---|
| 90 - 100 | A |
| 75 - 89 | B |
| 60 - 74 | C |
| 40 - 59 | D |
| Below 40 | Fail |
8.8 Importance of Condition Order
In an else-if ladder, conditions are checked from top to bottom. Once a condition is true, its block executes and the remaining conditions are skipped.
Correct approach
if (marks >= 90)
{
printf("A");
}
else if (marks >= 75)
{
printf("B");
}
else if (marks >= 60)
{
printf("C");
}
8.9 Nested if
A nested if is an if statement placed inside another if or else block.
int age = 25;
int hasID = 1;
if (age >= 18)
{
if (hasID)
{
printf("Entry allowed");
}
}
↓
YES
↓
hasID ?
↓
YES
↓
Entry allowed
8.10 Multiple Conditions
Logical operators can be combined with decision-making statements.
int age = 25;
if (age >= 18 && age <= 60)
{
printf("Eligible");
}
Using OR
int day = 1;
if (day == 1 || day == 7)
{
printf("Weekend");
}
Using NOT
int available = 0;
if (!available)
{
printf("Not available");
}
8.11 Problem: Largest of Two Numbers
int a, b;
scanf("%d %d", &a, &b);
if (a > b)
{
printf("%d", a);
}
else
{
printf("%d", b);
}
25 18
Output:
25
8.12 Problem: Largest of Three Numbers
int a, b, c;
scanf("%d %d %d", &a, &b, &c);
if (a >= b && a >= c)
{
printf("%d", a);
}
else if (b >= a && b >= c)
{
printf("%d", b);
}
else
{
printf("%d", c);
}
10 25 18
Output:
25
8.13 Problem: Leap Year
A year is a leap year if it is divisible by 400, or if it is divisible by 4 but not by 100.
int year;
scanf("%d", &year);
if (year % 400 == 0 ||
(year % 4 == 0 && year % 100 != 0))
{
printf("Leap Year");
}
else
{
printf("Not a Leap Year");
}
2024 → Leap Year
1900 → Not a Leap Year
2000 → Leap Year
8.14 switch Statement
The switch statement selects one block from multiple cases based on the value of an integer-compatible expression.
{
case value1:
statements;
break;
case value2:
statements;
break;
default:
statements;
}
8.15 switch Example
int day;
scanf("%d", &day);
switch(day)
{
case 1:
printf("Monday");
break;
case 2:
printf("Tuesday");
break;
case 3:
printf("Wednesday");
break;
default:
printf("Invalid day");
}
8.16 Why break is Used
The break statement terminates the switch statement and transfers control to the statement after the switch.
int n = 1;
switch(n)
{
case 1:
printf("One");
break;
case 2:
printf("Two");
break;
}
8.17 default in switch
The default label executes when none of the case values match.
int choice = 5;
switch(choice)
{
case 1:
printf("Add");
break;
case 2:
printf("Subtract");
break;
default:
printf("Invalid choice");
}
8.18 Calculator Using switch
int a, b;
char op;
scanf("%d %c %d", &a, &op, &b);
switch(op)
{
case '+':
printf("%d", a + b);
break;
case '-':
printf("%d", a - b);
break;
case '*':
printf("%d", a * b);
break;
case '/':
if (b != 0)
{
printf("%d", a / b);
}
else
{
printf("Division by zero is not allowed");
}
break;
default:
printf("Invalid operator");
}
8.19 if-else vs switch
| if-else | switch |
|---|---|
| Works well with ranges and complex conditions | Works well with discrete case values |
| Can use relational operators | Cases use constant values |
| Can combine conditions using logical operators | Useful for menu-style choices |
| Good for ranges such as marks >= 90 | Good for choices such as 1, 2, 3, 4 |
8.20 Menu-Driven Program
switch is commonly used to implement menu-driven programs.
int choice;
printf("1. Add\n");
printf("2. Subtract\n");
printf("3. Multiply\n");
printf("4. Exit\n");
scanf("%d", &choice);
switch(choice)
{
case 1:
printf("Addition selected");
break;
case 2:
printf("Subtraction selected");
break;
case 3:
printf("Multiplication selected");
break;
case 4:
printf("Exit");
break;
default:
printf("Invalid choice");
}
8.21 Nested Decision Example
Nested decisions are useful when one condition depends on another.
int marks;
scanf("%d", &marks);
if (marks >= 40)
{
if (marks >= 75)
{
printf("Distinction");
}
else
{
printf("Pass");
}
}
else
{
printf("Fail");
}
8.22 Common Mistakes
- Using = instead of == in a comparison.
- Forgetting braces when multiple statements belong to a condition.
- Writing incorrect condition order in an else-if ladder.
- Forgetting break in a switch case when fall-through is not intended.
- Forgetting the default case when invalid input should be handled.
- Using switch when range-based conditions are required.
- Forgetting to handle division by zero.
- Writing overly complicated nested if statements.
8.23 Problem-Solving Method
Before writing an if-else program, identify the condition first.
Step 2 → Identify the input
Step 3 → Identify the condition
Step 4 → Decide the possible outcomes
Step 5 → Write the condition
Step 6 → Test boundary cases
Example
Problem: Check whether a number is positive, negative or zero.
Condition 1 → n > 0
Condition 2 → n < 0
Otherwise → n == 0
8.24 Boundary Cases
Boundary testing is very important in programming problems.
| Problem | Important Test Cases |
|---|---|
| Even/Odd | 0, positive, negative |
| Positive/Negative | Positive, negative, 0 |
| Pass/Fail | 39, 40, 41 |
| Grade | 59, 60, 74, 75, 89, 90 |
| Leap Year | 1900, 2000, 2024 |
8.25 Quick Revision
📌 if-else → Two alternatives
📌 else-if → Multiple conditions
📌 Nested if → Decision inside another decision
📌 switch → Multiple discrete choices
📌 break → Exit switch
📌 default → No case matched
📌 Always test boundary cases.
8.26 Quick MCQs
-
Which statement is used to make a
decision based on a condition?
A) for
B) if
C) printf
D) scanf
Answer: B -
Which statement provides two alternatives?
A) if
B) if-else
C) switch only
D) for
Answer: B -
Which keyword terminates a switch case
when fall-through is not intended?
A) stop
B) exit
C) break
D) continue
Answer: C -
Which keyword handles unmatched switch
cases?
A) else
B) default
C) otherwise
D) none
Answer: B -
What is the output?
int n = 10;
if (n > 5)
{
printf("Yes");
}
A) No
B) Yes
C) 10
D) Error
Answer: B -
Which is best suited for checking
multiple fixed menu choices?
A) switch
B) while
C) do-while
D) continue
Answer: A
8.27 Practice Problems
Check whether a number is positive, negative or zero.
Problem 2
Check whether a number is even or odd.
Problem 3
Find the largest of two numbers.
Problem 4
Find the largest of three numbers.
Problem 5
Check whether a student has passed or failed.
Problem 6
Print the grade based on marks.
Problem 7
Check whether a year is a leap year.
Problem 8
Create a calculator using switch.
Problem 9
Create a menu-driven program for addition, subtraction and multiplication.
Problem 10
Check whether a person is eligible based on age and another condition.
Problem 11
Find whether a character is a vowel or consonant.
Problem 12
Check whether three sides can form a valid triangle.
8.28 Key Takeaway
Remember:
Problem → Condition → Decision → Output
Master if, if-else, else-if, nested if and switch before moving to loops.
🔁 9. Loops
9.1 What is a Loop?
A loop is a control structure that repeatedly executes a block of statements while a specified condition is satisfied.
Suppose you want to print:
Hello
Hello
Hello
Hello
Hello
Instead of writing printf() five times, we can use a loop.
9.2 Why Do We Need Loops?
Loops reduce repetitive code and make programs shorter, easier to maintain and easier to understand.
printf("Hello\n");
printf("Hello\n");
printf("Hello\n");
printf("Hello\n");
printf("Hello\n");
The same task can be performed using:
for(int i = 1; i <= 5; i++)
{
printf("Hello\n");
}
9.3 Types of Loops in C
| Loop | Condition Checked | Typical Use |
|---|---|---|
| for | Before each iteration | Known/reasonable iteration count |
| while | Before each iteration | Condition-controlled repetition |
| do-while | After each iteration | Execute body at least once |
9.4 for Loop
The for loop is commonly used when the number of iterations is known or can be expressed conveniently.
{
statements;
}
Example
for(int i = 1; i <= 5; i++)
{
printf("%d\n", i);
}
2
3
4
5
9.5 How for Loop Works
↓
Condition
↓
Execute Body
↓
Update
↓
Condition
↓
Repeat
Example: i = 1
| Iteration | i | Condition | Output |
|---|---|---|---|
| 1 | 1 | 1 <= 5 → True | 1 |
| 2 | 2 | 2 <= 5 → True | 2 |
| 3 | 3 | 3 <= 5 → True | 3 |
| 4 | 4 | 4 <= 5 → True | 4 |
| 5 | 5 | 5 <= 5 → True | 5 |
| 6 | 6 | 6 <= 5 → False | Stop |
9.6 while Loop
The while loop repeatedly executes a block while its condition remains true.
{
statements;
}
Example
int i = 1;
while(i <= 5)
{
printf("%d\n", i);
i++;
}
2
3
4
5
9.7 How while Loop Works
↓
Check Condition
↓
True?
↓
Execute Body
↓
Update
↓
Check Again
9.8 do-while Loop
The do-while loop executes its body first and checks the condition afterward.
{
statements;
}
while(condition);
Example
int i = 1;
do
{
printf("%d\n", i);
i++;
}
while(i <= 5);
2
3
4
5
9.9 Important Difference: while vs do-while
The key difference is when the condition is checked.
int i = 10;
while(i < 5)
{
printf("Hello");
}
int i = 10;
do
{
printf("Hello");
}
while(i < 5);
while → May execute zero times.
do-while → Executes at least once.
9.10 Counting with Loops
A common use of loops is counting from one number to another.
for(int i = 1; i <= 10; i++)
{
printf("%d ", i);
}
9.11 Counting in Reverse
for(int i = 10; i >= 1; i--)
{
printf("%d ", i);
}
9.12 Print Even Numbers
for(int i = 2; i <= 20; i += 2)
{
printf("%d ", i);
}
9.13 Print Odd Numbers
for(int i = 1; i <= 20; i += 2)
{
printf("%d ", i);
}
9.14 Sum of First N Numbers
We can use a loop to calculate the sum from 1 to N.
int n;
int sum = 0;
scanf("%d", &n);
for(int i = 1; i <= n; i++)
{
sum = sum + i;
}
printf("%d", sum);
5
Calculation:
1 + 2 + 3 + 4 + 5
Output:
15
9.15 Factorial
The factorial of a non-negative integer n is the product of all positive integers from 1 to n.
= 120
int n;
long long fact = 1;
scanf("%d", &n);
for(int i = 1; i <= n; i++)
{
fact = fact * i;
}
printf("%lld", fact);
9.16 Multiplication Table
int n;
scanf("%d", &n);
for(int i = 1; i <= 10; i++)
{
printf("%d x %d = %d\n",
n, i, n * i);
}
5
Output:
5 x 1 = 5
5 x 2 = 10
5 x 3 = 15
...
5 x 10 = 50
9.17 Number of Digits
We can repeatedly divide an integer by 10 to remove its last digit.
int n;
int count = 0;
scanf("%d", &n);
if(n == 0)
{
count = 1;
}
else
{
if(n < 0)
n = -n;
while(n != 0)
{
n = n / 10;
count++;
}
}
printf("%d", count);
Output: 4
9.18 Reverse a Number
To reverse a number, repeatedly extract the last digit using the modulus operator.
int n;
int reverse = 0;
scanf("%d", &n);
while(n != 0)
{
int digit = n % 10;
reverse = reverse * 10 + digit;
n = n / 10;
}
printf("%d", reverse);
1234
Output:
4321
9.19 Palindrome Number
A number is a palindrome if it reads the same from left to right and right to left.
123 → Not Palindrome
int n;
int original;
int reverse = 0;
scanf("%d", &n);
original = n;
while(n != 0)
{
int digit = n % 10;
reverse = reverse * 10 + digit;
n = n / 10;
}
if(original == reverse)
{
printf("Palindrome");
}
else
{
printf("Not Palindrome");
}
9.20 Prime Number
A prime number is an integer greater than 1 that has exactly two positive divisors: 1 and itself.
3 → Prime
4 → Not Prime
5 → Prime
int n;
int isPrime = 1;
scanf("%d", &n);
if(n < 2)
{
isPrime = 0;
}
else
{
for(int i = 2; i * i <= n; i++)
{
if(n % i == 0)
{
isPrime = 0;
break;
}
}
}
if(isPrime)
{
printf("Prime");
}
else
{
printf("Not Prime");
}
9.21 break Statement
The break statement immediately terminates the nearest enclosing loop.
for(int i = 1; i <= 10; i++)
{
if(i == 5)
{
break;
}
printf("%d ", i);
}
9.22 continue Statement
The continue statement skips the remaining statements in the current iteration and proceeds with the next iteration.
for(int i = 1; i <= 5; i++)
{
if(i == 3)
{
continue;
}
printf("%d ", i);
}
9.23 break vs continue
| break | continue |
|---|---|
| Terminates the loop | Skips current iteration |
| Control exits the loop | Control moves to next iteration |
| Used when further repetition is unnecessary | Used when one iteration should be skipped |
9.24 Nested Loops
A loop inside another loop is called a nested loop.
for(int i = 1; i <= 3; i++)
{
for(int j = 1; j <= 3; j++)
{
printf("* ");
}
printf("\n");
}
* * *
* * *
9.25 Pattern Printing
for(int i = 1; i <= 5; i++)
{
for(int j = 1; j <= i; j++)
{
printf("* ");
}
printf("\n");
}
* *
* * *
* * * *
* * * * *
9.26 Multiple Variables in for Loop
A for loop can contain more than one initialization or update expression.
for(int i = 1, j = 5;
i <= 5;
i++, j--)
{
printf("%d %d\n", i, j);
}
2 4
3 3
4 2
5 1
9.27 Infinite Loop
A loop that never becomes false is called an infinite loop.
while(1)
{
printf("Hello\n");
}
Always make sure that the loop condition can eventually become false unless an intentionally infinite loop is required.
9.28 Common Loop Mistakes
- Forgetting to initialize the loop variable.
- Forgetting to update the loop variable.
- Using the wrong loop condition.
- Creating an unintended infinite loop.
- Using i < n when i <= n is required, or vice versa.
- Incorrectly placing break or continue.
- Using the wrong variable inside nested loops.
- Forgetting that integer division removes the fractional part.
9.29 Which Loop Should I Use?
| Situation | Recommended Loop |
|---|---|
| Known number of repetitions | for |
| Condition-controlled repetition | while |
| Body must execute at least once | do-while |
| Pattern printing | Nested for |
| Searching until found | for / while with break |
9.30 Problem-Solving Method for Loops
Step 2 → Identify the starting value.
Step 3 → Identify the stopping condition.
Step 4 → Identify how the value changes.
Step 5 → Decide whether for, while or do-while is appropriate.
Step 6 → Test small values.
Example: Sum from 1 to N
Stop → i <= N
Change → i++
Operation → sum = sum + i
9.31 Quick Revision
📌 while → Condition checked before each iteration.
📌 do-while → Body executes before condition check.
📌 break → Exit loop.
📌 continue → Skip current iteration.
📌 Nested loops → Loop inside another loop.
📌 Always check initialization, condition and update.
9.32 Quick MCQs
-
Which loop is commonly used when the
number of iterations is known?
A) if
B) for
C) switch
D) goto
Answer: B -
Which loop executes its body at least
once?
A) for
B) while
C) do-while
D) if
Answer: C -
Which keyword terminates a loop?
A) continue
B) break
C) stop
D) exitloop
Answer: B -
Which keyword skips the current iteration?
A) break
B) skip
C) continue
D) next
Answer: C -
What is the output?
for(int i = 1; i <= 3; i++)
{
printf("%d ", i);
}
A) 0 1 2
B) 1 2 3
C) 1 2
D) 2 3 4
Answer: B -
How many times does this loop execute?
for(int i = 1; i <= 5; i++)
{
printf("*");
}
A) 4
B) 5
C) 6
D) Infinite
Answer: B
9.33 Practice Problems
Print numbers from 1 to N.
Problem 2
Print numbers from N to 1.
Problem 3
Print all even numbers from 1 to N.
Problem 4
Print all odd numbers from 1 to N.
Problem 5
Find the sum of the first N natural numbers.
Problem 6
Find the factorial of a number.
Problem 7
Print the multiplication table of a number.
Problem 8
Count the number of digits in an integer.
Problem 9
Reverse a number.
Problem 10
Check whether a number is a palindrome.
Problem 11
Check whether a number is prime.
Problem 12
Print all prime numbers from 1 to N.
Problem 13
Find the sum of digits of a number.
Problem 14
Find the largest digit in a number.
Problem 15
Count even and odd digits in a number.
Problem 16
Print the following pattern:
*
* *
* * *
* * * *
* * * * *
9.34 Key Takeaway
1. Initialization
Where does the loop start?
2. Condition
When should the loop continue?
3. Update
How does the loop variable change?
Remember:
Start → Check → Execute → Update → Repeat
Mastering loops is essential for solving programming problems involving numbers, digits, patterns, arrays and algorithms.
📊 10. Arrays
10.1 What is an Array?
An array is a collection of elements of the same data type stored in contiguous memory locations.
80, 75, 90, 65, 88
Instead of creating five separate variables:
mark1, mark2, mark3, mark4, mark5
we can use one array:
marks[5]
10.2 Why Do We Need Arrays?
Arrays allow us to store and process multiple values using a single variable name.
int marks[5];
marks[0] = 80;
marks[1] = 75;
marks[2] = 90;
marks[3] = 65;
marks[4] = 88;
10.3 Array Index
C arrays use zero-based indexing. This means the first element has index 0.
| Index | Value |
|---|---|
| 0 | 80 |
| 1 | 75 |
| 2 | 90 |
| 3 | 65 |
| 4 | 88 |
Second element → arr[1]
Last element → arr[n-1]
10.4 Array Declaration
Examples
int numbers[10];
float marks[5];
char letters[26];
double prices[20];
10.5 Array Initialization
int numbers[5] = {10, 20, 30, 40, 50};
The compiler assigns the values to indexes starting from 0.
numbers[1] = 20
numbers[2] = 30
numbers[3] = 40
numbers[4] = 50
10.6 Initialization Without Specifying Size
int numbers[] = {10, 20, 30, 40, 50};
The compiler determines the size from the number of initializers.
10.7 Partial Initialization
int numbers[5] = {10, 20};
The remaining elements are initialized to zero for this initialization form.
10.8 Accessing Array Elements
int numbers[5] = {10, 20, 30, 40, 50};
printf("%d", numbers[2]);
10.9 Modifying an Array Element
int numbers[5] = {10, 20, 30, 40, 50};
numbers[2] = 100;
printf("%d", numbers[2]);
10.10 Reading Array Elements
A loop is commonly used to read values into an array.
int n;
scanf("%d", &n);
int arr[n];
for(int i = 0; i < n; i++)
{
scanf("%d", &arr[i]);
}
10.11 Printing Array Elements
for(int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
↓
Traverse every element
↓
Process the element
10.12 Array Traversal
Traversing an array means visiting each element one by one.
int arr[] = {10, 20, 30, 40, 50};
int n = 5;
for(int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
10.13 Sum of Array Elements
int n;
int sum = 0;
scanf("%d", &n);
int arr[n];
for(int i = 0; i < n; i++)
{
scanf("%d", &arr[i]);
sum += arr[i];
}
printf("Sum = %d", sum);
5
10 20 30 40 50
Output:
Sum = 150
10.14 Average of Array Elements
int n;
int sum = 0;
scanf("%d", &n);
int arr[n];
for(int i = 0; i < n; i++)
{
scanf("%d", &arr[i]);
sum += arr[i];
}
double average = (double)sum / n;
printf("Average = %.2f", average);
10.15 Maximum Element
int n;
scanf("%d", &n);
int arr[n];
for(int i = 0; i < n; i++)
{
scanf("%d", &arr[i]);
}
int max = arr[0];
for(int i = 1; i < n; i++)
{
if(arr[i] > max)
{
max = arr[i];
}
}
printf("Maximum = %d", max);
5
10 45 20 80 30
Output:
Maximum = 80
10.16 Minimum Element
int min = arr[0];
for(int i = 1; i < n; i++)
{
if(arr[i] < min)
{
min = arr[i];
}
}
printf("Minimum = %d", min);
10.17 Count Even and Odd Elements
int even = 0;
int odd = 0;
for(int i = 0; i < n; i++)
{
if(arr[i] % 2 == 0)
{
even++;
}
else
{
odd++;
}
}
printf("Even = %d\n", even);
printf("Odd = %d", odd);
10.18 Searching in an Array
Searching means checking whether a particular value exists in the array.
10.19 Linear Search
Linear search checks each element one by one until the target is found or the array ends.
int key;
int found = 0;
scanf("%d", &key);
for(int i = 0; i < n; i++)
{
if(arr[i] == key)
{
found = 1;
break;
}
}
if(found)
{
printf("Element Found");
}
else
{
printf("Element Not Found");
}
10 20 30 40 50
Search:
30
Output:
Element Found
10.20 Find Position of an Element
int key;
int position = -1;
scanf("%d", &key);
for(int i = 0; i < n; i++)
{
if(arr[i] == key)
{
position = i;
break;
}
}
if(position != -1)
{
printf("Index = %d", position);
}
else
{
printf("Not Found");
}
10.21 Reverse an Array
for(int i = n - 1; i >= 0; i--)
{
printf("%d ", arr[i]);
}
10 20 30 40 50
Output:
50 40 30 20 10
10.22 Copy One Array to Another
int arr2[n];
for(int i = 0; i < n; i++)
{
arr2[i] = arr[i];
}
10.23 Count Positive, Negative and Zero
int positive = 0;
int negative = 0;
int zero = 0;
for(int i = 0; i < n; i++)
{
if(arr[i] > 0)
{
positive++;
}
else if(arr[i] < 0)
{
negative++;
}
else
{
zero++;
}
}
printf("Positive = %d\n", positive);
printf("Negative = %d\n", negative);
printf("Zero = %d", zero);
10.24 Second Largest Element
Finding the second largest element is an important problem-solving exercise.
int largest = arr[0];
int second = arr[0];
for(int i = 1; i < n; i++)
{
if(arr[i] > largest)
{
second = largest;
largest = arr[i];
}
else if(arr[i] > second &&
arr[i] != largest)
{
second = arr[i];
}
}
printf("Largest = %d\n", largest);
printf("Second Largest = %d", second);
10.25 Find Duplicate Elements
Nested loops can be used to compare every element with the elements after it.
for(int i = 0; i < n; i++)
{
for(int j = i + 1; j < n; j++)
{
if(arr[i] == arr[j])
{
printf("%d ", arr[i]);
break;
}
}
}
10.26 Find Unique Elements
An element is unique if it appears only once in the array.
for(int i = 0; i < n; i++)
{
int count = 0;
for(int j = 0; j < n; j++)
{
if(arr[i] == arr[j])
{
count++;
}
}
if(count == 1)
{
printf("%d ", arr[i]);
}
}
10.27 Frequency of an Element
int key;
int count = 0;
scanf("%d", &key);
for(int i = 0; i < n; i++)
{
if(arr[i] == key)
{
count++;
}
}
printf("Frequency = %d", count);
10.28 Largest and Smallest Together
int max = arr[0];
int min = arr[0];
for(int i = 1; i < n; i++)
{
if(arr[i] > max)
{
max = arr[i];
}
if(arr[i] < min)
{
min = arr[i];
}
}
printf("Maximum = %d\n", max);
printf("Minimum = %d", min);
10.29 Swapping Two Array Elements
int temp;
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
10.30 Reverse Array In-Place
int start = 0;
int end = n - 1;
while(start < end)
{
int temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
start++;
end--;
}
10.31 Introduction to Sorting
Sorting means arranging elements in a particular order.
| Order | Example |
|---|---|
| Ascending | 10 20 30 40 50 |
| Descending | 50 40 30 20 10 |
10.32 Bubble Sort
Bubble sort repeatedly compares adjacent elements and swaps them if they are in the wrong order.
for(int i = 0; i < n - 1; i++)
{
for(int j = 0; j < n - 1 - i; j++)
{
if(arr[j] > arr[j + 1])
{
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
10.33 Two-Dimensional Array
A two-dimensional array is commonly used to represent rows and columns, such as a matrix.
int matrix[3][3];
10.34 Reading a Matrix
int rows, cols;
scanf("%d %d", &rows, &cols);
int matrix[rows][cols];
for(int i = 0; i < rows; i++)
{
for(int j = 0; j < cols; j++)
{
scanf("%d", &matrix[i][j]);
}
}
10.35 Printing a Matrix
for(int i = 0; i < rows; i++)
{
for(int j = 0; j < cols; j++)
{
printf("%d ", matrix[i][j]);
}
printf("\n");
}
10.36 Matrix Addition
Two matrices can be added when they have the same dimensions.
for(int i = 0; i < rows; i++)
{
for(int j = 0; j < cols; j++)
{
result[i][j] =
matrix1[i][j] + matrix2[i][j];
}
}
10.37 Main Diagonal Elements
In a square matrix, main diagonal elements have equal row and column indexes.
for(int i = 0; i < n; i++)
{
printf("%d ", matrix[i][i]);
}
10.38 Row Sum
for(int i = 0; i < rows; i++)
{
int sum = 0;
for(int j = 0; j < cols; j++)
{
sum += matrix[i][j];
}
printf("Row %d Sum = %d\n",
i + 1, sum);
}
10.39 Column Sum
for(int j = 0; j < cols; j++)
{
int sum = 0;
for(int i = 0; i < rows; i++)
{
sum += matrix[i][j];
}
printf("Column %d Sum = %d\n",
j + 1, sum);
}
10.40 Array Problem-Solving Pattern
Step 2 → Read N elements
Step 3 → Traverse using a loop
Step 4 → Apply condition/calculation
Step 5 → Store the result if needed
Step 6 → Print the result
Example
Find the maximum element.
↓
Assume arr[0] is maximum
↓
Compare remaining elements
↓
Update maximum
↓
Print maximum
10.41 Common Array Mistakes
- Forgetting that array indexing starts from 0.
- Accessing an index outside the valid range.
- Using i <= n instead of i < n while traversing.
- Forgetting to initialize variables such as sum, count or maximum.
- Using an incorrect loop limit.
- Confusing array index with array value.
- Forgetting to check special cases such as an empty or too-small input where applicable.
10.42 Quick Revision
📌 Index starts at 0.
📌 Last index → n - 1.
📌 Traversal → Visit every element.
📌 Searching → Find an element.
📌 Sorting → Arrange elements.
📌 2D array → Rows and columns.
📌 Arrays + Loops are fundamental to problem solving.
10.43 Quick MCQs
-
What is the index of the first element
of a C array?
A) 0
B) 1
C) -1
D) 2
Answer: A -
What is the last index of an array
containing n elements?
A) n
B) n + 1
C) n - 1
D) 0
Answer: C -
Which loop is commonly used to traverse
an array?
A) for
B) switch
C) if
D) goto
Answer: A -
Which algorithm checks elements one by one
to find a target?
A) Binary search
B) Linear search
C) Merge sort
D) Selection sort
Answer: B -
Which array represents rows and columns?
A) 1D array
B) 2D array
C) Pointer
D) Structure
Answer: B
10.44 Practice Problems
Read and print N array elements.
Problem 2
Find the sum of array elements.
Problem 3
Find the average of array elements.
Problem 4
Find the maximum element.
Problem 5
Find the minimum element.
Problem 6
Count even and odd elements.
Problem 7
Count positive, negative and zero elements.
Problem 8
Search for an element using linear search.
Problem 9
Find the position of an element.
Problem 10
Reverse an array.
Problem 11
Copy one array into another.
Problem 12
Find duplicate elements.
Problem 13
Find unique elements.
Problem 14
Find the frequency of a given element.
Problem 15
Find the second largest element.
Problem 16
Sort an array in ascending order.
Problem 17
Sort an array in descending order.
Problem 18
Find the sum of each row of a matrix.
Problem 19
Find the sum of each column of a matrix.
Problem 20
Print the main diagonal of a square matrix.
10.45 Key Takeaway
Array → Loop → Process → Result
Once you understand this pattern, many array problems become much easier.
Arrays are the foundation for:
Searching → Sorting → Strings → Matrices → Data Structures → Algorithms
🔤 11. Strings
11.1 What is a String?
A string in C is a sequence of characters terminated by a special character called the null character \0.
"HELLO"
Internally, C stores it as:
H E L L O \0
C does not have a separate built-in string data type.
Strings are stored using character arrays.
11.2 Character vs String
| Character | String |
|---|---|
| 'A' | "A" |
| Single character | Sequence of characters |
| Uses single quotes | Uses double quotes |
| char | char array |
11.3 Character Array
A character array can be used to store a string.
char name[20];
This creates an array capable of storing characters.
11.4 String Initialization
char name[] = "Venu";
C automatically adds the null character \0 at the end.
11.5 String Size
The array must have enough space for all characters plus the null character.
char name[5] = "Venu";
Total = 5 characters
11.6 Printing a String
char name[] = "Venu";
printf("%s", name);
The %s format specifier is used to print a string.
11.7 Accessing Individual Characters
char name[] = "Venu";
printf("%c\n", name[0]);
printf("%c\n", name[1]);
printf("%c\n", name[2]);
printf("%c\n", name[3]);
e
n
u
11.8 Traversing a String
char str[] = "HELLO";
for(int i = 0; str[i] != '\0'; i++)
{
printf("%c ", str[i]);
}
11.9 Reading a Single Word using scanf()
char name[20];
scanf("%s", name);
printf("%s", name);
Venu
Output:
Venu
Input:
Venu Gopal
It reads only:
Venu
11.10 Reading a Line using fgets()
Use fgets() when you want to read a line that may contain spaces.
char name[50];
fgets(name, sizeof(name), stdin);
printf("%s", name);
Venu Gopal
Output:
Venu Gopal
11.11 String Library Functions
C provides several useful string functions through the <string.h> header file.
#include <string.h>
| Function | Purpose |
|---|---|
| strlen() | Find string length |
| strcpy() | Copy a string |
| strcat() | Concatenate strings |
| strcmp() | Compare strings |
| strchr() | Find a character |
| strstr() | Find a substring |
11.12 strlen()
The strlen() function returns the number of characters in a string, excluding the terminating \0.
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "HELLO";
printf("%zu", strlen(str));
return 0;
}
11.13 strcpy()
The strcpy() function copies the contents of one string into another character array.
char source[] = "Hello";
char destination[20];
strcpy(destination, source);
printf("%s", destination);
11.14 strcat()
The strcat() function appends one string to another.
char first[30] = "Hello ";
char second[] = "World";
strcat(first, second);
printf("%s", first);
11.15 strcmp()
The strcmp() function compares two strings lexicographically.
char a[] = "apple";
char b[] = "apple";
if(strcmp(a, b) == 0)
{
printf("Equal");
}
else
{
printf("Not Equal");
}
strcmp(a, b) == 0
Do NOT use:
a == b
to compare the contents of two strings.
11.16 Find String Length Without strlen()
char str[100];
scanf("%99s", str);
int length = 0;
while(str[length] != '\0')
{
length++;
}
printf("Length = %d", length);
11.17 Count Vowels
char str[100];
int vowels = 0;
scanf("%99s", str);
for(int i = 0; str[i] != '\0'; i++)
{
char ch = str[i];
if(ch == 'a' || ch == 'e' ||
ch == 'i' || ch == 'o' ||
ch == 'u' ||
ch == 'A' || ch == 'E' ||
ch == 'I' || ch == 'O' ||
ch == 'U')
{
vowels++;
}
}
printf("Vowels = %d", vowels);
11.18 Count Consonants
A consonant is an alphabetic character that is not a vowel.
char str[100];
int consonants = 0;
scanf("%99s", str);
for(int i = 0; str[i] != '\0'; i++)
{
char ch = str[i];
if((ch >= 'A' && ch <= 'Z') ||
(ch >= 'a' && ch <= 'z'))
{
if(!(ch == 'a' || ch == 'e' ||
ch == 'i' || ch == 'o' ||
ch == 'u' ||
ch == 'A' || ch == 'E' ||
ch == 'I' || ch == 'O' ||
ch == 'U'))
{
consonants++;
}
}
}
printf("Consonants = %d", consonants);
11.19 Count Digits in a String
char str[100];
int digits = 0;
scanf("%99s", str);
for(int i = 0; str[i] != '\0'; i++)
{
if(str[i] >= '0' && str[i] <= '9')
{
digits++;
}
}
printf("Digits = %d", digits);
11.20 Count Spaces
char str[100];
int spaces = 0;
fgets(str, sizeof(str), stdin);
for(int i = 0; str[i] != '\0'; i++)
{
if(str[i] == ' ')
{
spaces++;
}
}
printf("Spaces = %d", spaces);
11.21 Convert Lowercase to Uppercase
You can convert lowercase English letters using ASCII arithmetic.
char str[100];
scanf("%99s", str);
for(int i = 0; str[i] != '\0'; i++)
{
if(str[i] >= 'a' && str[i] <= 'z')
{
str[i] = str[i] - 'a' + 'A';
}
}
printf("%s", str);
11.22 Convert Uppercase to Lowercase
char str[100];
scanf("%99s", str);
for(int i = 0; str[i] != '\0'; i++)
{
if(str[i] >= 'A' && str[i] <= 'Z')
{
str[i] = str[i] - 'A' + 'a';
}
}
printf("%s", str);
11.23 Reverse a String
#include <string.h>
char str[100];
scanf("%99s", str);
int length = strlen(str);
for(int i = length - 1; i >= 0; i--)
{
printf("%c", str[i]);
}
HELLO
Output:
OLLEH
11.24 Palindrome String
A string is a palindrome if it reads the same from both directions.
HELLO → Not Palindrome
#include <string.h>
char str[100];
scanf("%99s", str);
int left = 0;
int right = strlen(str) - 1;
int palindrome = 1;
while(left < right)
{
if(str[left] != str[right])
{
palindrome = 0;
break;
}
left++;
right--;
}
if(palindrome)
{
printf("Palindrome");
}
else
{
printf("Not Palindrome");
}
11.25 Compare Two Strings Without strcmp()
char a[100];
char b[100];
scanf("%99s", a);
scanf("%99s", b);
int equal = 1;
int i = 0;
while(a[i] != '\0' || b[i] != '\0')
{
if(a[i] != b[i])
{
equal = 0;
break;
}
i++;
}
if(equal)
{
printf("Equal");
}
else
{
printf("Not Equal");
}
11.26 Copy String Without strcpy()
char source[100];
char destination[100];
scanf("%99s", source);
int i = 0;
while(source[i] != '\0')
{
destination[i] = source[i];
i++;
}
destination[i] = '\0';
printf("%s", destination);
11.27 Concatenate Without strcat()
char a[100];
char b[50];
scanf("%99s", a);
scanf("%49s", b);
int i = 0;
int j = 0;
while(a[i] != '\0')
{
i++;
}
while(b[j] != '\0')
{
a[i] = b[j];
i++;
j++;
}
a[i] = '\0';
printf("%s", a);
11.28 Frequency of a Character
char str[100];
char key;
scanf("%99s", str);
scanf(" %c", &key);
int count = 0;
for(int i = 0; str[i] != '\0'; i++)
{
if(str[i] == key)
{
count++;
}
}
printf("Frequency = %d", count);
11.29 Remove Spaces from a String
char str[200];
fgets(str, sizeof(str), stdin);
int j = 0;
for(int i = 0; str[i] != '\0'; i++)
{
if(str[i] != ' ')
{
str[j] = str[i];
j++;
}
}
str[j] = '\0';
printf("%s", str);
11.30 Count Words in a Sentence
A simple word-counting approach is to count transitions from whitespace to a non-whitespace character.
char str[200];
fgets(str, sizeof(str), stdin);
int words = 0;
int inWord = 0;
for(int i = 0; str[i] != '\0'; i++)
{
if(str[i] != ' ' &&
str[i] != '\n' &&
str[i] != '\t')
{
if(!inWord)
{
words++;
inWord = 1;
}
}
else
{
inWord = 0;
}
}
printf("Words = %d", words);
11.31 Check Whether a String Contains Only Alphabets
char str[100];
scanf("%99s", str);
int valid = 1;
for(int i = 0; str[i] != '\0'; i++)
{
if(!((str[i] >= 'A' && str[i] <= 'Z') ||
(str[i] >= 'a' && str[i] <= 'z')))
{
valid = 0;
break;
}
}
if(valid)
{
printf("Only Alphabets");
}
else
{
printf("Contains Non-Alphabet Characters");
}
11.32 First Non-Repeating Character
A nested-loop approach can be used to find the first character that appears only once.
char str[100];
scanf("%99s", str);
int found = 0;
for(int i = 0; str[i] != '\0'; i++)
{
int count = 0;
for(int j = 0; str[j] != '\0'; j++)
{
if(str[i] == str[j])
{
count++;
}
}
if(count == 1)
{
printf("%c", str[i]);
found = 1;
break;
}
}
if(!found)
{
printf("No non-repeating character");
}
11.33 Array of Strings
Multiple strings can be stored using a two-dimensional character array.
char names[3][20] =
{
"Venu",
"Ravi",
"Sita"
};
for(int i = 0; i < 3; i++)
{
printf("%s\n", names[i]);
}
Ravi
Sita
11.34 Important: scanf() vs fgets()
| Method | Spaces | Typical Use |
|---|---|---|
| scanf("%s", str) | Stops at whitespace | Single word |
| fgets() | Can read spaces | Full line |
11.35 Why is \0 Important?
String functions and many string operations need to know where the string ends.
C → A → T → \0
The \0 tells C that the string has ended.
'\0' → Null character
'0' → Character zero
11.36 Common String Mistakes
- Forgetting space for the null character.
- Using == to compare string contents.
- Using scanf("%s") when the input contains spaces.
- Reading more characters than the array can hold.
- Forgetting #include <string.h> when using standard string functions.
- Forgetting to add \0 when manually constructing a string.
- Confusing 'A' with "A".
11.37 Quick Revision
📌 Strings are stored as character arrays.
📌 Strings end with \0.
📌 %s is used for string output.
📌 strlen() → length.
📌 strcpy() → copy.
📌 strcat() → concatenate.
📌 strcmp() → compare.
📌 fgets() can read spaces.
11.38 Quick MCQs
-
Which character terminates a C string?
A) '\n'
B) '\0'
C) '0'
D) '\t'
Answer: B -
Which format specifier is used to print
a string?
A) %c
B) %d
C) %s
D) %f
Answer: C -
Which function finds the length of a string?
A) strcpy()
B) strlen()
C) strcat()
D) strcmp()
Answer: B -
Which function compares two strings?
A) strcpy()
B) strlen()
C) strcmp()
D) strcat()
Answer: C -
Which function concatenates strings?
A) strcat()
B) strlen()
C) strcmp()
D) strchr()
Answer: A -
Which function can read a line containing spaces?
A) scanf("%s")
B) fgets()
C) strlen()
D) strcpy()
Answer: B
11.39 Practice Problems
Read and print a string.
Problem 2
Find the length of a string without using strlen().
Problem 3
Count vowels in a string.
Problem 4
Count consonants in a string.
Problem 5
Count digits in a string.
Problem 6
Count spaces in a sentence.
Problem 7
Convert lowercase characters to uppercase.
Problem 8
Convert uppercase characters to lowercase.
Problem 9
Reverse a string.
Problem 10
Check whether a string is a palindrome.
Problem 11
Compare two strings without strcmp().
Problem 12
Copy one string to another without strcpy().
Problem 13
Concatenate two strings without strcat().
Problem 14
Find the frequency of a character.
Problem 15
Remove all spaces from a string.
Problem 16
Count the number of words in a sentence.
Problem 17
Check whether a string contains only alphabets.
Problem 18
Find the first non-repeating character.
Problem 19
Find the first repeating character.
Problem 20
Store and print 5 student names using a two-dimensional character array.
11.40 Key Takeaway
String → Character Array → Loop → Process
Example:
Find vowels
↓
Traverse characters
↓
Check each character
↓
Count vowels
↓
Print result
Mastering strings will make many coding-platform problems much easier.
🔧 12. Functions
12.1 What is a Function?
A function is a named block of code designed to perform a specific task.
Example:
main()
↓
calculateSum()
↓
findMaximum()
↓
printResult()
12.2 Why Do We Need Functions?
- Functions make programs easier to understand.
- They reduce code repetition.
- They make debugging easier.
- They improve code organization.
- They allow code reuse.
- Large problems can be divided into smaller problems.
Input → Function → Output
12.3 Basic Function Syntax
return_type function_name(parameters)
{
// statements
return value;
}
| Part | Meaning |
|---|---|
| return_type | Type of value returned by function |
| function_name | Name of the function |
| parameters | Input values received by function |
| statements | Work performed by function |
| return | Sends a value back to caller |
12.4 Simple Function
#include <stdio.h>
void greet()
{
printf("Hello!");
}
int main()
{
greet();
return 0;
}
12.5 Calling a Function
A function executes when it is called.
greet();
↓
Function Call
↓
Function Executes
12.6 Function Declaration / Prototype
A function prototype tells the compiler about the function before it is used.
int add(int, int);
Complete example:
#include <stdio.h>
int add(int, int);
int main()
{
int result = add(10, 20);
printf("%d", result);
return 0;
}
int add(int a, int b)
{
return a + b;
}
12.7 Function Definition
int add(int a, int b)
{
return a + b;
}
This contains the actual implementation of the function.
12.8 Function Call
int result = add(10, 20);
Here, 10 and 20 are arguments passed to the function.
12.9 Parameters and Arguments
int add(int a, int b)
{
return a + b;
}
int result = add(10, 20);
| Term | Example |
|---|---|
| Parameters | a, b |
| Arguments | 10, 20 |
12.10 Function with Return Value
int square(int n)
{
return n * n;
}
int main()
{
int result = square(5);
printf("%d", result);
return 0;
}
12.11 void Function
A void function does not return a value.
void display()
{
printf("Welcome to C");
}
12.12 Function Without Parameters
void message()
{
printf("Hello World");
}
int main()
{
message();
return 0;
}
12.13 Parameters Without Return Value
void printSum(int a, int b)
{
printf("%d", a + b);
}
int main()
{
printSum(10, 20);
return 0;
}
12.14 No Parameters With Return Value
int getNumber()
{
return 100;
}
int main()
{
int x = getNumber();
printf("%d", x);
return 0;
}
12.15 Four Common Types of Functions
| Type | Parameters | Return Value |
|---|---|---|
| Type 1 | No | No |
| Type 2 | Yes | No |
| Type 3 | No | Yes |
| Type 4 | Yes | Yes |
12.16 Add Two Numbers Using Function
int add(int a, int b)
{
return a + b;
}
int main()
{
int a, b;
scanf("%d %d", &a, &b);
printf("%d", add(a, b));
return 0;
}
12.17 Find Maximum Using Function
int maximum(int a, int b)
{
if(a > b)
return a;
return b;
}
int main()
{
int a, b;
scanf("%d %d", &a, &b);
printf("Maximum = %d", maximum(a, b));
return 0;
}
12.18 Check Even or Odd Using Function
int isEven(int n)
{
return n % 2 == 0;
}
int main()
{
int n;
scanf("%d", &n);
if(isEven(n))
printf("Even");
else
printf("Odd");
return 0;
}
12.19 Check Positive, Negative or Zero
void checkNumber(int n)
{
if(n > 0)
printf("Positive");
else if(n < 0)
printf("Negative");
else
printf("Zero");
}
int main()
{
int n;
scanf("%d", &n);
checkNumber(n);
return 0;
}
12.20 Check Prime Number Using Function
int isPrime(int n)
{
if(n < 2)
return 0;
for(int i = 2; i * i <= n; i++)
{
if(n % i == 0)
return 0;
}
return 1;
}
int main()
{
int n;
scanf("%d", &n);
if(isPrime(n))
printf("Prime");
else
printf("Not Prime");
return 0;
}
12.21 Factorial Using Function
long long factorial(int n)
{
long long fact = 1;
for(int i = 1; i <= n; i++)
{
fact *= i;
}
return fact;
}
int main()
{
int n;
scanf("%d", &n);
printf("%lld", factorial(n));
return 0;
}
12.22 GCD Using Function
The greatest common divisor can be calculated efficiently using the Euclidean algorithm.
int gcd(int a, int b)
{
while(b != 0)
{
int temp = b;
b = a % b;
a = temp;
}
return a;
}
int main()
{
int a, b;
scanf("%d %d", &a, &b);
printf("GCD = %d", gcd(a, b));
return 0;
}
12.23 LCM Using Function
int gcd(int a, int b)
{
while(b != 0)
{
int temp = b;
b = a % b;
a = temp;
}
return a;
}
long long lcm(int a, int b)
{
if(a == 0 || b == 0)
return 0;
return (long long)a / gcd(a, b) * b;
}
int main()
{
int a, b;
scanf("%d %d", &a, &b);
printf("LCM = %lld", lcm(a, b));
return 0;
}
12.24 What is Recursion?
Recursion occurs when a function calls itself.
↓
Calls itself
↓
Smaller problem
↓
Base case
↓
Stop
12.25 Recursive Factorial
long long factorial(int n)
{
if(n <= 1)
return 1;
return n * factorial(n - 1);
}
int main()
{
int n;
scanf("%d", &n);
printf("%lld", factorial(n));
return 0;
}
12.26 Recursive Fibonacci
int fibonacci(int n)
{
if(n == 0)
return 0;
if(n == 1)
return 1;
return fibonacci(n - 1)
+ fibonacci(n - 2);
}
int main()
{
int n;
scanf("%d", &n);
printf("%d", fibonacci(n));
return 0;
}
12.27 Sum of First N Natural Numbers
int sumN(int n)
{
if(n == 0)
return 0;
return n + sumN(n - 1);
}
int main()
{
int n;
scanf("%d", &n);
printf("%d", sumN(n));
return 0;
}
12.28 Passing an Array to a Function
An array can be passed to a function along with its size.
int sumArray(int arr[], int n)
{
int sum = 0;
for(int i = 0; i < n; i++)
{
sum += arr[i];
}
return sum;
}
int main()
{
int arr[] = {10, 20, 30, 40, 50};
int n = 5;
printf("%d", sumArray(arr, n));
return 0;
}
12.29 Maximum Element Using Function
int maximum(int arr[], int n)
{
int max = arr[0];
for(int i = 1; i < n; i++)
{
if(arr[i] > max)
{
max = arr[i];
}
}
return max;
}
int main()
{
int arr[] = {10, 50, 20, 80, 30};
int n = 5;
printf("Maximum = %d",
maximum(arr, n));
return 0;
}
12.30 Passing a String to a Function
#include <stdio.h>
void display(char str[])
{
printf("%s", str);
}
int main()
{
char name[] = "Venu";
display(name);
return 0;
}
12.31 Scope of Variables
Scope determines where a variable can be accessed.
| Type | Meaning |
|---|---|
| Local variable | Declared inside a function/block |
| Global variable | Declared outside functions |
12.32 Local Variable
void test()
{
int x = 10;
printf("%d", x);
}
The variable x is local to the function.
12.33 Global Variable
#include <stdio.h>
int x = 100;
void display()
{
printf("%d", x);
}
int main()
{
display();
return 0;
}
12.34 static Variable
A local static variable retains its stored value between function calls.
void counter()
{
static int count = 0;
count++;
printf("%d\n", count);
}
int main()
{
counter();
counter();
counter();
return 0;
}
2
3
12.35 Call by Value
In C, ordinary function arguments are passed by value. The function receives a copy of the argument.
void change(int x)
{
x = 100;
}
int main()
{
int a = 10;
change(a);
printf("%d", a);
return 0;
}
12.36 Modifying a Variable Using a Pointer
To modify the caller's variable, we can pass its address using a pointer.
void change(int *x)
{
*x = 100;
}
int main()
{
int a = 10;
change(&a);
printf("%d", a);
return 0;
}
12.37 Swap Two Numbers Using Function
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
int main()
{
int a, b;
scanf("%d %d", &a, &b);
swap(&a, &b);
printf("%d %d", a, b);
return 0;
}
12.38 How Function Calling Works
↓
call function
↓
function receives arguments
↓
function performs task
↓
return result
↓
main() continues
12.39 How to Design a Good Function
- Give the function one clear responsibility.
- Use a meaningful function name.
- Keep the function reasonably small.
- Pass only the data the function needs.
- Return a useful result when appropriate.
- Avoid unnecessary global variables.
12.40 Function-Based Problem Solving
↓
Break into smaller tasks
↓
Create functions
↓
Call functions
↓
Combine results
Instead of putting everything inside main():
readArray()
sumArray()
calculateAverage()
printResult()
12.41 Common Function Mistakes
- Calling a function before declaring it when no suitable declaration is visible.
- Using the wrong return type.
- Forgetting to return a value from a non-void function.
- Passing the wrong number or type of arguments.
- Forgetting that ordinary C arguments are passed by value.
- Creating recursion without a proper base case.
- Using too many global variables.
- Giving one function too many unrelated responsibilities.
12.42 Quick Revision
📌 Prototype → Function declaration.
📌 Parameters → Variables in function definition.
📌 Arguments → Actual values passed during the call.
📌 return → Sends a value back.
📌 void → No return value.
📌 Recursion → Function calling itself.
📌 Local variable → Limited to its scope.
📌 Global variable → Declared outside functions.
📌 static local variable → Retains its value between calls.
12.43 Quick MCQs
-
What is a function?
A) A variable
B) A reusable block of code
C) A data type
D) An operator
Answer: B -
Which keyword indicates that a function
returns no value?
A) null
B) empty
C) void
D) zero
Answer: C -
What is recursion?
A) A loop
B) A function calling itself
C) A variable assignment
D) An array
Answer: B -
What should recursive functions normally
have to stop recursion?
A) Pointer
B) Array
C) Base case
D) Structure
Answer: C -
Which keyword is used to send a value back
from a function?
A) break
B) return
C) continue
D) goto
Answer: B -
Ordinary C function arguments are generally
passed:
A) By value
B) By name
C) By class
D) By object
Answer: A
12.44 Practice Problems
Create a function to print "Hello World".
Problem 2
Create a function to add two numbers.
Problem 3
Create a function to subtract two numbers.
Problem 4
Create a function to find the maximum of two numbers.
Problem 5
Check whether a number is even or odd using a function.
Problem 6
Check whether a number is positive, negative or zero.
Problem 7
Check whether a number is prime using a function.
Problem 8
Find factorial using a function.
Problem 9
Find GCD using a function.
Problem 10
Find LCM using a function.
Problem 11
Find the sum of first N natural numbers using recursion.
Problem 12
Find factorial using recursion.
Problem 13
Find the Nth Fibonacci number using recursion.
Problem 14
Find the sum of an array using a function.
Problem 15
Find the maximum element of an array using a function.
Problem 16
Find the minimum element of an array using a function.
Problem 17
Pass a string to a function and print it.
Problem 18
Check whether a string is a palindrome using a function.
Problem 19
Swap two numbers using a function and pointers.
Problem 20
Create a menu-driven calculator using separate functions for addition, subtraction, multiplication and division.
12.45 Key Takeaway
Large Problem → Break into Smaller Problems → Create Functions → Call Functions → Combine Results
Functions are the foundation for writing clean, reusable and modular C programs.
👉 13. Pointers
13.1 What is a Pointer?
A pointer is a variable that stores the memory address of another variable.
int x = 10;
int *p = &x;
&x → address of x
p → stores the address of x
*p → value stored at that address → 10
13.2 Why Do We Need Pointers?
- To work directly with memory addresses.
- To modify variables inside functions.
- To efficiently work with arrays and strings.
- To dynamically allocate memory.
- To create data structures such as linked lists and trees.
- To work with structures and system-level programming.
13.3 Understanding Memory
Every variable is stored somewhere in computer memory. That location has an address.
↓
Memory Address
↓
Stored Value
int x = 10;
printf("Value = %d\n", x);
printf("Address = %p\n", (void*)&x);
13.4 Address Operator &
The & operator gives the memory address of a variable.
int x = 25;
printf("%p", (void*)&x);
&x = address of x
13.5 Pointer Declaration
int *p;
This declares p as a pointer to an integer.
| Declaration | Pointer Points To |
|---|---|
| int *p; | int |
| char *p; | char |
| float *p; | float |
| double *p; | double |
13.6 Pointer Initialization
int x = 10;
int *p = &x;
↓
address of x
↓
x = 10
13.7 Dereference Operator *
The * operator is used to access the value stored at the address held by a pointer.
int x = 10;
int *p = &x;
printf("%d", *p);
*p → value at address
13.8 Complete Pointer Example
#include <stdio.h>
int main()
{
int x = 50;
int *p = &x;
printf("Value of x = %d\n", x);
printf("Address of x = %p\n", (void*)&x);
printf("Value stored in p = %p\n", (void*)p);
printf("Value using *p = %d\n", *p);
return 0;
}
&x → address of x
p → stores address of x
*p → value of x
13.9 Modifying a Variable Using a Pointer
int x = 10;
int *p = &x;
*p = 100;
printf("%d", x);
Changing *p changes the original variable because the pointer points to that variable.
```html13.10 Pointer Memory Diagram
x stores the value 10.
&x gives the address of x.
p stores the address of x.
*p gives the value stored at that address → 10.
The addresses 1000 and 2000 are only illustrative. Actual memory addresses are different.
13.11 Pointer Types
int x = 10;
char c = 'A';
float f = 3.14f;
int *p1 = &x;
char *p2 = &c;
float *p3 = &f;
The pointer type should correspond to the type of object it points to.
13.12 NULL Pointer
A null pointer is a pointer that intentionally does not point to a valid object.
int *p = NULL;
Before dereferencing a pointer, make sure it points to a valid object.
if(p != NULL)
{
printf("%d", *p);
}
13.13 Pointer and Array
The name of an array can be used in many expressions as a pointer to its first element.
int arr[] = {10, 20, 30};
printf("%d", *arr);
arr refers to the first element in this expression.
13.14 Accessing Array Using Pointer
int arr[] = {10, 20, 30, 40};
int *p = arr;
for(int i = 0; i < 4; i++)
{
printf("%d ", *(p + i));
}
13.15 Pointer Arithmetic
Pointers can be incremented and decremented. The movement is based on the size of the pointed-to type.
int arr[] = {10, 20, 30};
int *p = arr;
printf("%d\n", *p);
p++;
printf("%d\n", *p);
20
13.16 Pointer Expressions
*(p + 0)
*(p + 1)
*(p + 2)
13.17 Pointer Difference
Pointers to elements of the same array can be subtracted to determine the number of elements between them.
int arr[] = {10, 20, 30, 40};
int *p = &arr[0];
int *q = &arr[3];
printf("%td", q - p);
13.18 Passing Pointer to a Function
void change(int *p)
{
*p = 100;
}
int main()
{
int x = 10;
change(&x);
printf("%d", x);
return 0;
}
13.19 Swap Two Numbers Using Pointers
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
int main()
{
int x = 10;
int y = 20;
swap(&x, &y);
printf("%d %d", x, y);
return 0;
}
13.20 Pointer and String
char str[] = "HELLO";
char *p = str;
while(*p != '\0')
{
printf("%c ", *p);
p++;
}
13.21 Pointer to String Literal
const char *p = "Hello";
printf("%s", p);
Using const char * communicates that the characters of the string literal should not be modified through the pointer.
13.22 Pointer to Pointer
A pointer can itself have an address, so another pointer can store its address.
int x = 10;
int *p = &x;
int **q = &p;
printf("%d\n", x);
printf("%d\n", *p);
printf("%d\n", **q);
10
10
**q → value of x
13.23 sizeof Pointer
int *p;
printf("%zu", sizeof(p));
The size of a pointer depends on the system and implementation. It is not necessarily the same as the size of the object it points to.
13.24 Dynamic Memory Allocation
Dynamic memory allows a program to request memory during runtime.
The main functions are:
- malloc()
- calloc()
- realloc()
- free()
These functions are declared in <stdlib.h>.
13.25 malloc()
malloc() allocates a requested number of bytes and returns a pointer to the allocated memory if successful.
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *p = malloc(5 * sizeof(int));
if(p == NULL)
{
return 1;
}
for(int i = 0; i < 5; i++)
{
p[i] = (i + 1) * 10;
}
for(int i = 0; i < 5; i++)
{
printf("%d ", p[i]);
}
free(p);
return 0;
}
13.26 calloc()
calloc() allocates space for multiple elements and initializes the allocated bytes to zero.
int *p = calloc(5, sizeof(int));
if(p == NULL)
{
return 1;
}
for(int i = 0; i < 5; i++)
{
printf("%d ", p[i]);
}
free(p);
13.27 realloc()
realloc() changes the size of a previously allocated memory block.
int *p = malloc(3 * sizeof(int));
if(p == NULL)
{
return 1;
}
p[0] = 10;
p[1] = 20;
p[2] = 30;
int *temp = realloc(p, 5 * sizeof(int));
if(temp != NULL)
{
p = temp;
p[3] = 40;
p[4] = 50;
}
free(p);
13.28 free()
free() releases dynamically allocated memory.
int *p = malloc(10 * sizeof(int));
if(p != NULL)
{
free(p);
p = NULL;
}
13.29 Dangling Pointer
A dangling pointer is a pointer that refers to an object or memory region that is no longer valid.
int *p = malloc(sizeof(int));
if(p != NULL)
{
free(p);
p = NULL;
}
Setting the pointer to NULL after freeing helps avoid accidentally using the old address.
13.30 Wild Pointer
A pointer that has not been initialized may contain an indeterminate value and must not be dereferenced.
int *p;
/* Do not do: *p = 10; */
Initialize pointers before using them.
int *p = NULL;
13.31 Pointer and const
const can be used in different ways with pointers.
const int *p;
The pointed-to integer should not be modified through p.
int *const p = &x;
The pointer itself cannot be redirected to another address after initialization.
13.32 Array of Pointers
int a = 10;
int b = 20;
int c = 30;
int *p[3] = {&a, &b, &c};
for(int i = 0; i < 3; i++)
{
printf("%d ", *p[i]);
}
13.33 Pointer to an Array
int arr[3] = {10, 20, 30};
int (*p)[3] = &arr;
printf("%d", (*p)[1]);
13.34 Pointer with Structure
struct Student
{
int age;
};
int main()
{
struct Student s = {20};
struct Student *p = &s;
printf("%d", p->age);
return 0;
}
The -> operator is commonly used to access structure members through a pointer.
13.35 Function Pointer Introduction
A function pointer stores the address of a function.
int add(int a, int b)
{
return a + b;
}
int main()
{
int (*fp)(int, int) = add;
printf("%d", fp(10, 20));
return 0;
}
13.36 Comparing Pointers
Pointers can be compared for equality or inequality. Relational comparisons are meaningful when pointers refer to elements of the same array.
int arr[3] = {10, 20, 30};
int *p = &arr[0];
int *q = &arr[0];
if(p == q)
{
printf("Same address");
}
13.37 Pointer with 2D Array
int arr[2][3] =
{
{10, 20, 30},
{40, 50, 60}
};
for(int i = 0; i < 2; i++)
{
for(int j = 0; j < 3; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
40 50 60
13.38 Pointer Formula
&x → address of x
int *p = &x;
p → address of x
*p → value of x
*p = 50 → changes x to 50
13.39 Common Pointer Mistakes
- Dereferencing an uninitialized pointer.
- Dereferencing a NULL pointer.
- Accessing memory after it has been freed.
- Forgetting to free dynamically allocated memory.
- Going outside the bounds of an array using pointers.
- Using the wrong pointer type.
- Confusing & with *.
- Confusing p with *p.
13.40 Quick Revision
📌 &x → address of x.
📌 *p → value at address stored in p.
📌 Pointer arithmetic is commonly used with arrays.
📌 Arrays and pointers are closely related, but they are not identical concepts.
📌 Pointers allow functions to modify caller data when addresses are passed.
📌 malloc() → dynamic allocation.
📌 calloc() → zero-initialized allocation.
📌 realloc() → resize allocation.
📌 free() → release allocation.
📌 NULL pointer → intentionally points to no valid object.
13.41 Quick MCQs
-
What does a pointer store?
A) Only integers
B) A memory address
C) A function name only
D) A keyword
Answer: B -
Which operator obtains the address of a variable?
A) *
B) &
C) %
D) #
Answer: B -
Which operator dereferences a pointer?
A) *
B) &
C) #
D) %
Answer: A -
What does NULL represent for a pointer?
A) It points to integer zero as an object
B) It intentionally points to no valid object
C) It always points to address 1
D) It is a string
Answer: B -
Which function allocates dynamic memory?
A) printf()
B) malloc()
C) scanf()
D) strlen()
Answer: B -
Which function releases dynamically allocated memory?
A) delete()
B) remove()
C) free()
D) clear()
Answer: C
13.42 Practice Problems
Print the value and address of a variable.
Problem 2
Access a variable's value using a pointer.
Problem 3
Modify a variable using a pointer.
Problem 4
Find the sum of two numbers using pointers.
Problem 5
Swap two numbers using pointers.
Problem 6
Find the maximum of two numbers using pointers.
Problem 7
Print all elements of an array using a pointer.
Problem 8
Find the sum of an array using pointer arithmetic.
Problem 9
Find the maximum element of an array using pointers.
Problem 10
Reverse an array using pointers.
Problem 11
Find the length of a string using a pointer.
Problem 12
Reverse a string using a pointer.
Problem 13
Count vowels in a string using a pointer.
Problem 14
Check whether a string is a palindrome using pointers.
Problem 15
Create a pointer to a pointer and print a variable's value.
Problem 16
Dynamically allocate an integer array using malloc().
Problem 17
Allocate an array using calloc() and print its initial values.
Problem 18
Resize a dynamically allocated array using realloc().
Problem 19
Create a structure and access its members using a structure pointer.
Problem 20
Create a function pointer for addition and use it to calculate the sum of two numbers.
13.43 Key Takeaway
&x → Address of x
p → Address stored in p
*p → Value at that address
Once this becomes clear, pointers become much easier to understand.
🏗️ 14. Structures & Unions
14.1 What is a Structure?
A structure is a user-defined data type that allows us to group different types of data under one name.
For example, a student may have:
- Name → character array
- Age → integer
- Marks → float
- Roll number → integer
Instead of storing these as unrelated variables, we can group them using a structure.
├── Roll Number
├── Name
├── Age
└── Marks
14.2 Structure Syntax
struct Student
{
int roll;
char name[50];
float marks;
};
Here Student is the structure tag.
14.3 Creating a Structure Variable
struct Student s1;
Now s1 is a variable of type struct Student.
14.4 Accessing Structure Members
The dot . operator is used to access members of a structure variable.
#include <stdio.h>
struct Student
{
int roll;
char name[50];
float marks;
};
int main()
{
struct Student s1;
s1.roll = 101;
s1.marks = 85.5;
printf("Roll = %d\n", s1.roll);
printf("Marks = %.2f\n", s1.marks);
return 0;
}
Marks = 85.50
14.5 Assigning a String to a Structure Member
A character array cannot normally be assigned using the = operator after declaration. Use strcpy() instead.
#include <stdio.h>
#include <string.h>
struct Student
{
int roll;
char name[50];
};
int main()
{
struct Student s1;
s1.roll = 101;
strcpy(s1.name, "Venu");
printf("%d\n", s1.roll);
printf("%s\n", s1.name);
return 0;
}
Venu
14.6 Structure Initialization
struct Student
{
int roll;
char name[50];
float marks;
};
struct Student s1 =
{
101,
"Venu",
85.5
};
14.7 Designated Initialization
C also allows members to be initialized by name.
struct Student s1 =
{
.roll = 101,
.name = "Venu",
.marks = 85.5
};
14.8 Taking Input into a Structure
#include <stdio.h>
struct Student
{
int roll;
char name[50];
float marks;
};
int main()
{
struct Student s;
scanf("%d", &s.roll);
scanf("%49s", s.name);
scanf("%f", &s.marks);
printf("Roll = %d\n", s.roll);
printf("Name = %s\n", s.name);
printf("Marks = %.2f\n", s.marks);
return 0;
}
14.9 Array of Structures
We can create an array containing multiple structure variables.
struct Student
{
int roll;
char name[50];
float marks;
};
struct Student students[3];
14.10 Example: Multiple Students
#include <stdio.h>
struct Student
{
int roll;
char name[50];
float marks;
};
int main()
{
struct Student s[3];
for(int i = 0; i < 3; i++)
{
scanf("%d", &s[i].roll);
scanf("%49s", s[i].name);
scanf("%f", &s[i].marks);
}
for(int i = 0; i < 3; i++)
{
printf("%d %s %.2f\n",
s[i].roll,
s[i].name,
s[i].marks);
}
return 0;
}
14.11 Passing Structure to a Function
#include <stdio.h>
struct Student
{
int roll;
float marks;
};
void display(struct Student s)
{
printf("Roll = %d\n", s.roll);
printf("Marks = %.2f\n", s.marks);
}
int main()
{
struct Student s = {101, 90.5};
display(s);
return 0;
}
14.12 Returning a Structure from a Function
#include <stdio.h>
struct Point
{
int x;
int y;
};
struct Point createPoint()
{
struct Point p = {10, 20};
return p;
}
int main()
{
struct Point p = createPoint();
printf("%d %d", p.x, p.y);
return 0;
}
14.13 Pointer to Structure
struct Student
{
int roll;
float marks;
};
int main()
{
struct Student s = {101, 85.5};
struct Student *p = &s;
printf("%d\n", p->roll);
printf("%.2f\n", p->marks);
return 0;
}
14.14 Arrow Operator ->
When we have a pointer to a structure, we can use -> to access its members.
struct Student *p = &s;
p->roll;
The following two expressions are equivalent:
p->roll
(*p).roll
📌 Structure pointer → use ->
14.15 Nested Structure
A structure can contain another structure as a member.
struct Date
{
int day;
int month;
int year;
};
struct Student
{
int roll;
char name[50];
struct Date dob;
};
14.16 Accessing Nested Structure
struct Student s =
{
101,
"Venu",
{5, 8, 2000}
};
printf("%d", s.dob.year);
14.17 Structure Containing an Array
struct Student
{
int roll;
char name[50];
int marks[5];
};
A structure member can itself be an array.
14.18 Real-World Example: Employee
struct Employee
{
int id;
char name[50];
float salary;
};
int main()
{
struct Employee e =
{
101,
"Ravi",
45000
};
printf("ID = %d\n", e.id);
printf("Name = %s\n", e.name);
printf("Salary = %.2f\n", e.salary);
return 0;
}
14.19 Structure vs Array
| Structure | Array |
|---|---|
| Can store different data types | Normally stores elements of one type |
| Members have names | Elements use indexes |
| Useful for records | Useful for collections of similar elements |
| Example: Student record | Example: Marks list |
14.20 What is a Union?
A union is a user-defined data type similar to a structure, but all members share the same memory location.
union Data
{
int i;
float f;
char c;
};
14.21 Union Example
#include <stdio.h>
union Data
{
int i;
float f;
char c;
};
int main()
{
union Data d;
d.i = 100;
printf("%d\n", d.i);
d.f = 3.14f;
printf("%.2f\n", d.f);
return 0;
}
A union should generally be read through the member that was most recently written, subject to the rules of the C standard.
14.22 Structure Memory
In a structure, each member has its own storage.
struct Data
{
int a;
float b;
char c;
};
a → storage
b → storage
c → storage
14.23 Union Memory
In a union, all members share the same memory location. Only one member's value is normally stored at a time.
14.24 Structure vs Union
| Feature | Structure | Union |
|---|---|---|
| Memory | Separate storage for members | Shared storage |
| Members usable simultaneously | Yes | Only one stored value at a time |
| Size | Generally reflects all members plus padding | Generally based on the largest member plus alignment |
| Typical use | Records | Memory-efficient alternatives / variant data |
14.25 typedef with Structure
typedef can create a convenient alias for a type.
typedef struct
{
int roll;
char name[50];
} Student;
Now we can write:
Student s1;
instead of:
struct Student s1;
14.26 typedef Example
#include <stdio.h>
typedef struct
{
int id;
float salary;
} Employee;
int main()
{
Employee e = {101, 45000};
printf("%d\n", e.id);
printf("%.2f\n", e.salary);
return 0;
}
14.27 Self-Referential Structure
A structure can contain a pointer to another object of the same structure type.
struct Node
{
int data;
struct Node *next;
};
14.28 Structure Padding
The compiler may insert unused bytes between structure members to satisfy alignment requirements.
struct Example
{
char c;
int x;
};
Therefore, you should not assume that the structure size is always exactly the sum of the individual member sizes.
printf("%zu", sizeof(struct Example));
14.29 Copying Structures
Structures of the same type can be assigned directly.
struct Student s1 = {101, "Venu", 90};
struct Student s2;
s2 = s1;
The members are copied as part of the structure assignment.
14.30 Comparing Structures
C does not provide a general == operator for comparing two structures.
Compare the required members individually.
if(s1.roll == s2.roll &&
s1.marks == s2.marks)
{
printf("Equal");
}
14.31 Common Structure Mistakes
- Forgetting the semicolon after the structure definition.
- Using . incorrectly with a structure pointer.
- Using -> with an ordinary structure variable.
- Trying to assign a character array using = after declaration.
- Assuming structure size is always the sum of member sizes.
- Reading a union member different from the one most recently written without understanding the relevant C rules.
14.32 Quick Revision
📌 Structure member access → .
📌 Structure pointer access → ->
📌 Array of structures → Stores multiple records.
📌 Nested structure → Structure inside another structure.
📌 Union → Members share the same memory.
📌 typedef → Creates a convenient type alias.
📌 Self-referential structure → Important for linked lists.
14.33 Quick MCQs
-
Which keyword is used to define a structure?
A) record
B) struct
C) structure
D) object
Answer: B -
Which operator accesses a structure member?
A) .
B) ->
C) *
D) &
Answer: A -
Which operator is used with a pointer to a structure?
A) .
B) ->
C) ::
D) %
Answer: B -
What is shared by members of a union?
A) Different arrays
B) Same memory location
C) Different functions
D) Different files
Answer: B -
Which keyword creates a type alias?
A) alias
B) typedef
C) rename
D) type
Answer: B -
Which structure is important for linked lists?
A) Nested structure only
B) Self-referential structure
C) Empty structure
D) Union
Answer: B
14.34 Practice Problems
Create a structure to store student details.
Problem 2
Read and display one student's details.
Problem 3
Store details of 5 students using an array of structures.
Problem 4
Find the student with the highest marks.
Problem 5
Find the student with the lowest marks.
Problem 6
Calculate the average marks of students.
Problem 7
Search for a student by roll number.
Problem 8
Sort students according to their marks.
Problem 9
Create an Employee structure and calculate annual salary.
Problem 10
Pass a structure to a function and display its members.
Problem 11
Return a structure from a function.
Problem 12
Access structure members using a structure pointer.
Problem 13
Create a nested structure for Student and Date of Birth.
Problem 14
Create a structure containing an array of 5 marks.
Problem 15
Create an Employee structure using typedef.
Problem 16
Create a union containing int, float and char.
Problem 17
Demonstrate the difference between structure and union memory usage using sizeof().
Problem 18
Create a self-referential Node structure.
Problem 19
Create a structure for a bank account and calculate the final balance.
Problem 20
Create a student management program using structures with options to add, display and search students.
14.35 Key Takeaway
Structure = Different data + Separate storage
Union = Different data + Shared storage
Structure variable → .
Structure pointer → ->
Self-referential structure → Foundation of Linked Lists
⚙️ 15. Preprocessor & Header Files
15.1 What is the C Preprocessor?
The C preprocessor is a program that processes source code before the actual compilation begins.
Preprocessor directives begin with the # symbol.
#include <stdio.h>
#define PI 3.14159
↓
Preprocessor
↓
Compiler
↓
Object Code
↓
Executable
15.2 Common Preprocessor Directives
| Directive | Purpose |
|---|---|
| #include | Includes a header file |
| #define | Defines a macro |
| #undef | Removes a macro definition |
| #if | Conditional compilation |
| #ifdef | Checks whether a macro is defined |
| #ifndef | Checks whether a macro is not defined |
| #else | Alternative conditional section |
| #elif | Another conditional branch |
| #endif | Ends conditional compilation |
15.3 #include
The #include directive tells the preprocessor to include the contents of another file.
#include <stdio.h>
This allows us to use functions such as printf() and scanf().
15.4 System Header Files
Standard C libraries provide commonly used functions through header files.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
| Header | Examples |
|---|---|
| stdio.h | printf(), scanf(), fopen() |
| stdlib.h | malloc(), free(), rand() |
| string.h | strlen(), strcpy(), strcmp() |
| math.h | sqrt(), pow(), sin() |
| ctype.h | isdigit(), isalpha(), toupper() |
| time.h | time(), clock() |
15.5 <> vs " " in #include
There are two common forms of the include directive.
#include <stdio.h>
#include "myheader.h"
| Syntax | Typical Use |
|---|---|
| #include <file.h> | System / standard headers |
| #include "file.h" | Project / local headers |
15.6 #define
The #define directive creates a macro.
#define PI 3.14159
Wherever the macro name appears later in the source, the preprocessor substitutes its replacement text.
#include <stdio.h>
#define PI 3.14159
int main()
{
printf("%f", PI);
return 0;
}
15.7 Object-like Macro
#define MAX 100
#define MIN 0
#define COLLEGE "ABC College"
These are called object-like macros because they do not take arguments.
15.8 Macro with Expression
#define PI 3.14159
#define LIMIT 50
#define SIZE 10
15.9 Function-like Macro
A macro can accept arguments.
#define SQUARE(x) ((x) * (x))
printf("%d", SQUARE(5));
15.10 Why Parentheses Matter in Macros
Always use parentheses carefully when writing expression macros.
Prefer:
#define SQUARE(x) ((x) * (x))
instead of:
#define SQUARE(x) x * x
The parenthesized version avoids many operator precedence problems when the macro is used inside larger expressions.
15.11 Macro with Multiple Arguments
#define MAX(a, b) ((a) > (b) ? (a) : (b))
printf("%d", MAX(10, 20));
15.12 Macro vs Function
| Macro | Function |
|---|---|
| Processed by preprocessor | Compiled as a function |
| No normal type checking for arguments | Arguments have declared types |
| Text substitution | Function call |
| Can cause repeated evaluation of arguments | Arguments are evaluated according to function-call rules |
15.13 Macro Side Effects
Be careful when passing expressions with side effects to function-like macros.
#define SQUARE(x) ((x) * (x))
int i = 5;
int result = SQUARE(i++);
This is dangerous because the argument may be expanded more than once.
15.14 #undef
The #undef directive removes a previously defined macro.
#define SIZE 100
#undef SIZE
15.15 Conditional Compilation
Conditional compilation allows parts of the source code to be included or excluded depending on preprocessor conditions.
#if condition
/* code */
#endif
15.16 #if
#define VERSION 2
#if VERSION == 2
printf("Version 2");
#endif
The code between #if and #endif is included only when the condition evaluates as true.
15.17 #if and #else
#define DEBUG 0
#if DEBUG
printf("Debug mode");
#else
printf("Normal mode");
#endif
15.18 #elif
#define VERSION 2
#if VERSION == 1
printf("Version 1");
#elif VERSION == 2
printf("Version 2");
#else
printf("Other version");
#endif
15.19 #ifdef
#ifdef checks whether a macro is defined.
#define DEBUG
#ifdef DEBUG
printf("Debug mode enabled");
#endif
15.20 #ifndef
#ifndef checks whether a macro is not defined.
#ifndef DEBUG
printf("DEBUG is not defined");
#endif
15.21 Header Guards
Header guards prevent the same header file from being included multiple times in a single compilation unit.
#ifndef STUDENT_H
#define STUDENT_H
struct Student
{
int roll;
char name[50];
};
#endif
15.22 Creating Your Own Header File
We can create our own header file containing declarations and reusable code.
Example file:
mathutils.h
Content:
#ifndef MATHUTILS_H
#define MATHUTILS_H
int add(int a, int b);
int subtract(int a, int b);
#endif
15.23 Using a User-defined Header
Create a source file containing the function definitions.
#include "mathutils.h"
int add(int a, int b)
{
return a + b;
}
int subtract(int a, int b)
{
return a - b;
}
15.24 Including Your Header in main()
#include <stdio.h>
#include "mathutils.h"
int main()
{
printf("%d\n", add(10, 20));
printf("%d\n", subtract(20, 10));
return 0;
}
10
15.25 Header File Structure
├── main.c
├── mathutils.c
└── mathutils.h
This separation makes larger programs easier to organize and maintain.
15.26 Standard vs User-defined Header
| Standard Header | User-defined Header |
|---|---|
| Provided by the C library | Created by the programmer |
| Example: stdio.h | Example: mathutils.h |
| Usually included with <> | Usually included with " " |
15.27 #pragma
#pragma provides implementation-specific instructions to the compiler.
Its exact behavior depends on the compiler and platform.
#pragma once
Many compilers support #pragma once as a way to prevent multiple inclusion of a header.
15.28 Predefined Macros
C implementations provide several predefined macros.
#include <stdio.h>
int main()
{
printf("File: %s\n", __FILE__);
printf("Line: %d\n", __LINE__);
printf("Date: %s\n", __DATE__);
printf("Time: %s\n", __TIME__);
return 0;
}
Common predefined macros include:
- __FILE__ → current source file name
- __LINE__ → current line number
- __DATE__ → compilation date
- __TIME__ → compilation time
15.29 assert.h
The assert.h header provides the assert() macro for debugging checks.
#include <assert.h>
int x = 10;
assert(x > 0);
If the expression is false, the assertion normally reports a diagnostic and terminates the program.
15.30 Common Preprocessor Mistakes
- Forgetting the # before a directive.
- Writing a function-like macro without careful parentheses.
- Passing expressions with side effects to macros.
- Including the wrong header file.
- Forgetting header guards.
- Assuming #pragma behavior is portable.
- Using macros when a normal function would be clearer and safer.
15.31 Quick Revision
📌 #include → Includes a file.
📌 #define → Defines a macro.
📌 #undef → Removes a macro.
📌 #if → Conditional compilation.
📌 #ifdef → Macro is defined.
📌 #ifndef → Macro is not defined.
📌 #else → Alternative condition.
📌 #elif → Additional condition.
📌 #endif → Ends conditional block.
📌 Header guard → Prevents repeated inclusion.
15.32 Quick MCQs
-
Which symbol begins a preprocessor directive?
A) $
B) #
C) @
D) %
Answer: B -
Which directive includes a header file?
A) #define
B) #include
C) #ifdef
D) #undef
Answer: B -
Which directive defines a macro?
A) #define
B) #include
C) #if
D) #endif
Answer: A -
Which directive removes a macro definition?
A) #remove
B) #delete
C) #undef
D) #clear
Answer: C -
Which directive checks whether a macro is defined?
A) #ifdef
B) #ifndef
C) #check
D) #defined
Answer: A -
Which directive checks whether a macro is not defined?
A) #ifdef
B) #ifndef
C) #notdefined
D) #ifnot
Answer: B -
Which header is commonly used for printf()?
A) string.h
B) stdio.h
C) math.h
D) stdlib.h
Answer: B -
Which directive ends a conditional compilation block?
A) #end
B) #endif
C) #stop
D) #finish
Answer: B
15.33 Practice Problems
Define a macro PI and calculate the area of a circle.
Problem 2
Create a macro to calculate the square of a number.
Problem 3
Create a macro to find the maximum of two numbers.
Problem 4
Create a macro to find the minimum of two numbers.
Problem 5
Create a macro to calculate the cube of a number.
Problem 6
Use #undef to remove a macro definition.
Problem 7
Use #ifdef to check whether DEBUG is defined.
Problem 8
Use #ifndef to conditionally compile a block of code.
Problem 9
Use #if and #else to select between two versions of a program.
Problem 10
Use #elif to select among three program versions.
Problem 11
Create a header file containing an add() function declaration.
Problem 12
Create a header file containing mathematical constants.
Problem 13
Create a user-defined header for string utility functions.
Problem 14
Create a header file using traditional include guards.
Problem 15
Create a program that displays __FILE__ and __LINE__.
Problem 16
Create a program using __DATE__ and __TIME__.
Problem 17
Create a macro that calculates the absolute value of a number.
Problem 18
Create a macro to check whether a number is even.
Problem 19
Demonstrate the difference between a macro and a function.
Problem 20
Create a small multi-file C project using a .c file, a .h file and main.c.
15.34 Key Takeaway
#include → Include files
#define → Define macros
#undef → Remove macros
#ifdef → If macro is defined
#ifndef → If macro is not defined
#if / #elif / #else → Conditional compilation
#endif → End conditional block
Header guards → Prevent repeated inclusion
📁 16. File Handling in C
16.1 What is File Handling?
File handling allows a C program to store data in a file and retrieve that data later.
Normally, variables store data temporarily in memory. When the program ends, that data is lost. Files provide a way to store data persistently.
↓
File
↓
Data stored permanently
16.2 File Pointer
C uses a special pointer called a file pointer to work with files.
FILE *fp;
The type FILE is defined in stdio.h.
#include <stdio.h>
int main()
{
FILE *fp;
return 0;
}
16.3 Opening a File - fopen()
The fopen() function is used to open a file.
FILE *fp;
fp = fopen("data.txt", "r");
The first argument is the file name and the second argument is the opening mode.
16.4 Checking Whether a File Opened Successfully
Always check whether fopen() returned NULL.
#include <stdio.h>
int main()
{
FILE *fp;
fp = fopen("data.txt", "r");
if(fp == NULL)
{
printf("File could not be opened");
return 1;
}
printf("File opened successfully");
fclose(fp);
return 0;
}
16.5 Closing a File - fclose()
After finishing file operations, close the file using fclose().
fclose(fp);
16.6 File Opening Modes
| Mode | Purpose |
|---|---|
| "r" | Open an existing file for reading |
| "w" | Open for writing; creates or truncates the file |
| "a" | Open for appending; creates if needed |
| "r+" | Read and write an existing file |
| "w+" | Read and write; creates or truncates |
| "a+" | Read and append; creates if needed |
| "rb" | Read a binary file |
| "wb" | Write a binary file |
| "ab" | Append to a binary file |
16.7 Writing to a File - fprintf()
The fprintf() function writes formatted data to a file.
#include <stdio.h>
int main()
{
FILE *fp;
fp = fopen("student.txt", "w");
if(fp == NULL)
{
printf("Unable to open file");
return 1;
}
fprintf(fp, "Name: Venu\n");
fprintf(fp, "Marks: 85\n");
fclose(fp);
return 0;
}
The file student.txt will contain:
Marks: 85
16.8 Writing a Character - fputc()
#include <stdio.h>
int main()
{
FILE *fp;
fp = fopen("data.txt", "w");
if(fp == NULL)
return 1;
fputc('A', fp);
fputc('B', fp);
fputc('C', fp);
fclose(fp);
return 0;
}
The file will contain:
16.9 Writing a String - fputs()
#include <stdio.h>
int main()
{
FILE *fp;
fp = fopen("data.txt", "w");
if(fp == NULL)
return 1;
fputs("Welcome to C Programming\n", fp);
fclose(fp);
return 0;
}
16.10 Reading from a File - fscanf()
The fscanf() function reads formatted data from a file.
#include <stdio.h>
int main()
{
FILE *fp;
char name[50];
int marks;
fp = fopen("student.txt", "r");
if(fp == NULL)
return 1;
fscanf(fp, "%49s %d", name, &marks);
printf("Name: %s\n", name);
printf("Marks: %d\n", marks);
fclose(fp);
return 0;
}
16.11 Reading a Character - fgetc()
#include <stdio.h>
int main()
{
FILE *fp;
int ch;
fp = fopen("data.txt", "r");
if(fp == NULL)
return 1;
ch = fgetc(fp);
if(ch != EOF)
printf("%c", ch);
fclose(fp);
return 0;
}
16.12 EOF - End of File
EOF represents the end-of-file condition.
Functions such as fgetc() return an int so that they can represent every possible unsigned character value as well as EOF.
int ch;
while((ch = fgetc(fp)) != EOF)
{
putchar(ch);
}
16.13 Reading a Complete File
#include <stdio.h>
int main()
{
FILE *fp;
int ch;
fp = fopen("data.txt", "r");
if(fp == NULL)
{
printf("File not found");
return 1;
}
while((ch = fgetc(fp)) != EOF)
{
putchar(ch);
}
fclose(fp);
return 0;
}
16.14 Reading a String - fgets()
fgets() reads a line or part of a line from a file into a character array.
#include <stdio.h>
int main()
{
FILE *fp;
char line[100];
fp = fopen("data.txt", "r");
if(fp == NULL)
return 1;
while(fgets(line, sizeof line, fp) != NULL)
{
printf("%s", line);
}
fclose(fp);
return 0;
}
16.15 Append Data - "a"
Append mode adds new data to the end of an existing file.
#include <stdio.h>
int main()
{
FILE *fp;
fp = fopen("data.txt", "a");
if(fp == NULL)
return 1;
fprintf(fp, "New record added\n");
fclose(fp);
return 0;
}
16.16 Write Mode - "w"
Write mode creates a file if it does not exist. If the file already exists, opening it in "w" mode truncates its previous contents.
fp = fopen("data.txt", "w");
16.17 Read Mode - "r"
Read mode opens an existing file for reading.
fp = fopen("data.txt", "r");
If the file does not exist or cannot be opened, fopen() returns NULL.
16.18 File Position Indicator
C maintains a position indicator associated with an open stream. Reading and writing normally move this position forward.
↓
[A] [B] [C] [D] [E]
↑
Current Position
16.19 fseek()
The fseek() function changes the file position indicator.
fseek(fp, 0, SEEK_SET);
Common origins are:
- SEEK_SET → beginning of file
- SEEK_CUR → current position
- SEEK_END → end of file
16.20 ftell()
ftell() returns the current file position indicator as a value of type long on typical implementations.
long position;
position = ftell(fp);
printf("Position = %ld", position);
16.21 rewind()
The rewind() function moves the file position indicator back to the beginning.
rewind(fp);
It also clears the stream's error and end-of-file indicators.
16.22 Binary Files
Binary files store data as bytes rather than as human-readable text.
Binary mode is commonly used for structures and other data where preserving a byte representation is useful.
fp = fopen("data.dat", "wb");
16.23 fwrite()
fwrite() writes blocks of bytes to a file.
#include <stdio.h>
struct Student
{
int roll;
float marks;
};
int main()
{
FILE *fp;
struct Student s = {101, 85.5};
fp = fopen("student.dat", "wb");
if(fp == NULL)
return 1;
fwrite(&s, sizeof s, 1, fp);
fclose(fp);
return 0;
}
16.24 fread()
fread() reads blocks of bytes from a file into memory.
#include <stdio.h>
struct Student
{
int roll;
float marks;
};
int main()
{
FILE *fp;
struct Student s;
fp = fopen("student.dat", "rb");
if(fp == NULL)
return 1;
if(fread(&s, sizeof s, 1, fp) == 1)
{
printf("%d %.2f", s.roll, s.marks);
}
fclose(fp);
return 0;
}
16.25 Text File vs Binary File
| Text File | Binary File |
|---|---|
| Human-readable representation | Raw bytes |
| Can be opened easily in a text editor | May not be meaningful in a text editor |
| Often larger for numeric data | Can be more compact for certain data |
| Example: .txt | Example: .dat |
16.26 Copy One File to Another
#include <stdio.h>
int main()
{
FILE *source;
FILE *destination;
int ch;
source = fopen("source.txt", "rb");
destination = fopen("copy.txt", "wb");
if(source == NULL || destination == NULL)
{
printf("Unable to open file");
return 1;
}
while((ch = fgetc(source)) != EOF)
{
fputc(ch, destination);
}
fclose(source);
fclose(destination);
return 0;
}
16.27 Count Characters in a File
#include <stdio.h>
int main()
{
FILE *fp;
int ch;
long count = 0;
fp = fopen("data.txt", "r");
if(fp == NULL)
return 1;
while((ch = fgetc(fp)) != EOF)
{
count++;
}
printf("Characters = %ld", count);
fclose(fp);
return 0;
}
16.28 Count Lines in a File
#include <stdio.h>
int main()
{
FILE *fp;
int ch;
int lines = 0;
fp = fopen("data.txt", "r");
if(fp == NULL)
return 1;
while((ch = fgetc(fp)) != EOF)
{
if(ch == '\n')
{
lines++;
}
}
printf("Lines = %d", lines);
fclose(fp);
return 0;
}
16.29 File Error Handling
Always check the return value of file operations.
FILE *fp = fopen("data.txt", "r");
if(fp == NULL)
{
perror("data.txt");
return 1;
}
perror() prints a message describing the most recent error associated with certain library or system operations.
16.30 Common File Handling Mistakes
- Forgetting to check whether fopen() returned NULL.
- Forgetting to close an opened file.
- Using "w" when you actually want to preserve existing contents.
- Using the wrong file mode.
- Using a character type instead of int when checking the result of fgetc() against EOF.
- Ignoring the return value of fread() or other important file operations.
- Assuming binary files are portable across all machines when writing raw C structures.
16.31 Quick Revision
📌 fopen() → Opens a file
📌 fclose() → Closes a file
📌 fprintf() → Writes formatted data
📌 fscanf() → Reads formatted data
📌 fputc() → Writes one character
📌 fgetc() → Reads one character
📌 fputs() → Writes a string
📌 fgets() → Reads a line/string
📌 fread() → Reads binary data
📌 fwrite() → Writes binary data
📌 fseek() → Moves file position
📌 ftell() → Gets current position
📌 rewind() → Returns to beginning
📌 EOF → End-of-file condition
16.32 Quick MCQs
-
Which type is used for a file pointer?
A) FILE *
B) file *
C) FilePointer
D) pointer_file
Answer: A -
Which function opens a file?
A) open()
B) fopen()
C) fileopen()
D) create()
Answer: B -
Which function closes a file?
A) close()
B) fclose()
C) fileclose()
D) endfile()
Answer: B -
Which mode opens a file for reading?
A) "r"
B) "w"
C) "a"
D) "x"
Answer: A -
Which mode can erase existing contents when opening
a file?
A) "r"
B) "w"
C) "a"
D) "rb"
Answer: B -
Which mode is used to append data?
A) "r"
B) "w"
C) "a"
D) "x"
Answer: C -
Which function writes formatted data to a file?
A) printf()
B) fprintf()
C) fwrite()
D) fput()
Answer: B -
Which function reads one character from a file?
A) fgets()
B) fscanf()
C) fgetc()
D) fread()
Answer: C -
Which function writes binary blocks?
A) fwrite()
B) fprintf()
C) fputs()
D) fputc()
Answer: A -
Which function changes the file position?
A) ftell()
B) fseek()
C) rewindfile()
D) move()
Answer: B
16.33 Practice Problems
Create and open a text file for writing.
Problem 2
Write a student's name and marks to a file.
Problem 3
Read and display the contents of a text file.
Problem 4
Write characters to a file using fputc().
Problem 5
Read characters from a file using fgetc().
Problem 6
Write a string to a file using fputs().
Problem 7
Read lines from a file using fgets().
Problem 8
Count the number of characters in a file.
Problem 9
Count the number of lines in a file.
Problem 10
Count the number of words in a text file.
Problem 11
Append a new record to an existing file.
Problem 12
Copy the contents of one file into another file.
Problem 13
Store student details in a binary file.
Problem 14
Read student details from a binary file.
Problem 15
Demonstrate fseek() using a text file.
Problem 16
Find the current file position using ftell().
Problem 17
Move the file position to the beginning using rewind().
Problem 18
Create a program that searches for a word in a text file.
Problem 19
Create a program that stores 10 student records in a file and displays them.
Problem 20
Create a simple Student Record Management System using file handling with options to add, display, search and update records.
16.34 Key Takeaway
FILE * → File pointer
fopen() → Open
fclose() → Close
fprintf() → Formatted write
fscanf() → Formatted read
fputc() → Write character
fgetc() → Read character
fputs() → Write string
fgets() → Read line
fwrite() → Binary write
fread() → Binary read
fseek() → Move position
ftell() → Current position
rewind() → Beginning
🧠 17. Dynamic Memory Allocation
17.1 What is Dynamic Memory Allocation?
Dynamic Memory Allocation allows a C program to request memory during program execution.
Unlike fixed-size arrays, dynamically allocated memory can be requested according to the actual requirements of the program.
↓
Request Memory
↓
Heap Memory
↓
Use Memory
↓
free()
17.2 Static vs Dynamic Memory
| Static / Automatic Allocation | Dynamic Allocation |
|---|---|
| Size is usually determined before execution of that block | Memory can be requested during execution |
| Commonly used with fixed-size arrays and local variables | Useful when the required size is known only at runtime |
| Managed automatically for automatic variables | Programmer explicitly manages allocated storage |
| Does not use malloc()/calloc() | Uses malloc(), calloc(), realloc() and free() |
17.3 Stack and Heap Memory
Dynamic memory is generally allocated from the heap.
The exact memory layout is implementation-dependent, but the stack and heap are useful concepts for understanding C memory management.
17.4 malloc()
malloc() allocates a block of uninitialized memory of the requested size.
#include <stdlib.h>
int *ptr;
ptr = malloc(5 * sizeof *ptr);
Here, memory is requested for five integers.
17.5 Example Using malloc()
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *ptr;
ptr = malloc(5 * sizeof *ptr);
if(ptr == NULL)
{
printf("Memory allocation failed");
return 1;
}
for(int i = 0; i < 5; i++)
{
ptr[i] = (i + 1) * 10;
}
for(int i = 0; i < 5; i++)
{
printf("%d ", ptr[i]);
}
free(ptr);
return 0;
}
17.6 Does malloc() Initialize Memory?
No. The bytes returned by malloc() have indeterminate values until your program writes appropriate values into them.
int *ptr = malloc(5 * sizeof *ptr);
Do not assume the allocated integers are initialized to zero.
17.7 calloc()
calloc() allocates memory for multiple elements and initializes the allocated bytes to zero.
int *ptr;
ptr = calloc(5, sizeof *ptr);
The first argument specifies the number of elements. The second specifies the size of each element.
17.8 Example Using calloc()
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *ptr;
ptr = calloc(5, sizeof *ptr);
if(ptr == NULL)
{
printf("Memory allocation failed");
return 1;
}
for(int i = 0; i < 5; i++)
{
printf("%d ", ptr[i]);
}
free(ptr);
return 0;
}
17.9 malloc() vs calloc()
| malloc() | calloc() |
|---|---|
| One size argument | Number of elements and element size |
| Memory is not initialized to zero | Allocated bytes are initialized to zero |
| malloc(size) | calloc(count, size) |
17.10 realloc()
realloc() changes the size of a previously allocated memory block.
ptr = realloc(ptr, 10 * sizeof *ptr);
The block may be moved to a different memory location. Therefore, use the pointer returned by realloc().
17.11 Example Using realloc()
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *ptr;
ptr = malloc(3 * sizeof *ptr);
if(ptr == NULL)
return 1;
for(int i = 0; i < 3; i++)
{
ptr[i] = (i + 1) * 10;
}
int *temp = realloc(ptr, 5 * sizeof *ptr);
if(temp == NULL)
{
free(ptr);
return 1;
}
ptr = temp;
ptr[3] = 40;
ptr[4] = 50;
for(int i = 0; i < 5; i++)
{
printf("%d ", ptr[i]);
}
free(ptr);
return 0;
}
17.12 Safe Use of realloc()
Do not directly overwrite your only pointer with realloc() if you need to handle allocation failure safely.
Prefer a temporary pointer:
int *temp;
temp = realloc(ptr, new_size);
if(temp != NULL)
{
ptr = temp;
}
else
{
/* Original ptr is still valid */
}
17.13 free()
free() releases dynamically allocated memory back to the implementation.
free(ptr);
After freeing memory, the pointer value becomes indeterminate. If you keep the pointer variable, assigning NULL to it can help prevent accidental reuse.
free(ptr);
ptr = NULL;
17.14 Dynamic Memory Diagram
17.15 Checking for NULL
Dynamic allocation functions return a null pointer if the allocation fails.
int *ptr = malloc(100 * sizeof *ptr);
if(ptr == NULL)
{
printf("Memory allocation failed");
return 1;
}
17.16 Dynamic Array
Dynamic allocation is useful when the number of elements is known only during program execution.
#include <stdio.h>
#include <stdlib.h>
int main()
{
int n;
printf("Enter number of elements: ");
scanf("%d", &n);
int *arr = malloc(n * sizeof *arr);
if(arr == NULL)
return 1;
for(int i = 0; i < n; i++)
{
scanf("%d", &arr[i]);
}
for(int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
free(arr);
return 0;
}
17.17 Dynamic Array Representation
17.18 Dynamically Allocated String
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
const char *text = "Hello";
char *str = malloc(strlen(text) + 1);
if(str == NULL)
return 1;
strcpy(str, text);
printf("%s", str);
free(str);
return 0;
}
17.19 Dynamic Structure
Structures can also be allocated dynamically.
#include <stdio.h>
#include <stdlib.h>
struct Student
{
int roll;
float marks;
};
int main()
{
struct Student *s;
s = malloc(sizeof *s);
if(s == NULL)
return 1;
s->roll = 101;
s->marks = 85.5;
printf("Roll = %d\n", s->roll);
printf("Marks = %.2f\n", s->marks);
free(s);
return 0;
}
17.20 Accessing Dynamic Structures
When a pointer points to a structure, the -> operator is used to access its members.
s->roll = 101;
s->marks = 85.5;
This is equivalent to:
(*s).roll = 101;
(*s).marks = 85.5;
17.21 Dynamic Array of Structures
#include <stdio.h>
#include <stdlib.h>
struct Student
{
int roll;
float marks;
};
int main()
{
int n;
scanf("%d", &n);
struct Student *students =
malloc(n * sizeof *students);
if(students == NULL)
return 1;
for(int i = 0; i < n; i++)
{
scanf("%d %f",
&students[i].roll,
&students[i].marks);
}
for(int i = 0; i < n; i++)
{
printf("%d %.2f\n",
students[i].roll,
students[i].marks);
}
free(students);
return 0;
}
17.22 Dynamic 2D Array
A two-dimensional array can also be allocated dynamically. One simple method is to allocate one contiguous block.
#include <stdio.h>
#include <stdlib.h>
int main()
{
int rows = 3;
int cols = 4;
int *matrix =
malloc(rows * cols * sizeof *matrix);
if(matrix == NULL)
return 1;
for(int i = 0; i < rows; i++)
{
for(int j = 0; j < cols; j++)
{
matrix[i * cols + j] = i + j;
}
}
for(int i = 0; i < rows; i++)
{
for(int j = 0; j < cols; j++)
{
printf("%d ", matrix[i * cols + j]);
}
printf("\n");
}
free(matrix);
return 0;
}
17.23 Memory Leak
A memory leak occurs when dynamically allocated memory is no longer reachable by the program and has not been released.
int *ptr = malloc(100 * sizeof *ptr);
/* ptr is lost without calling free() */
ptr = NULL;
17.24 Dangling Pointer
A dangling pointer is a pointer that refers to memory whose lifetime has ended or that has already been freed.
int *ptr = malloc(sizeof *ptr);
*ptr = 10;
free(ptr);
/* ptr should not be dereferenced here */
A common defensive practice is:
free(ptr);
ptr = NULL;
17.25 Double Free
Calling free() more than once on the same allocation is invalid.
free(ptr);
/* Do not do this again: */
free(ptr);
Setting the pointer to NULL after freeing can help avoid accidental repeated freeing, because free(NULL) has no effect.
17.26 Use-After-Free
Accessing memory after it has been released is invalid.
free(ptr);
/* Invalid */
printf("%d", *ptr);
17.27 Using sizeof() Correctly
Prefer using the pointed-to type when allocating memory.
int *ptr;
ptr = malloc(10 * sizeof *ptr);
This style is easier to maintain if the pointer type changes later.
17.28 Dynamic Memory Workflow
↓
malloc() / calloc()
↓
Check NULL
↓
Use Memory
↓
realloc() if required
↓
free()
17.29 Important Functions
| Function | Purpose |
|---|---|
| malloc() | Allocates uninitialized memory |
| calloc() | Allocates memory and initializes bytes to zero |
| realloc() | Changes the size of an allocation |
| free() | Releases allocated memory |
17.30 Common Dynamic Memory Mistakes
- Forgetting to check whether allocation returned NULL.
- Forgetting to call free().
- Accessing memory after free().
- Freeing the same allocation twice.
- Writing beyond the allocated block.
- Losing the only pointer to allocated memory.
- Using the wrong size in malloc() or calloc().
- Overwriting the original pointer with realloc() without considering allocation failure.
17.31 Quick Revision
📌 calloc() → Allocates and zero-initializes bytes
📌 realloc() → Resizes an allocation
📌 free() → Releases memory
📌 NULL → Indicates a null pointer
📌 Heap → Commonly used for dynamic storage
📌 Memory leak → Allocated memory is not released
📌 Dangling pointer → Pointer refers to an object whose lifetime has ended
📌 Use-after-free → Accessing freed memory
📌 sizeof *ptr → Useful for type-safe allocation sizing
17.32 Quick MCQs
-
Which function allocates dynamic memory?
A) malloc()
B) allocate()
C) memory()
D) new()
Answer: A -
Which function releases dynamically allocated memory?
A) delete()
B) remove()
C) free()
D) release()
Answer: C -
Which function allocates and zero-initializes the
allocated bytes?
A) malloc()
B) calloc()
C) realloc()
D) free()
Answer: B -
Which function changes the size of an allocation?
A) malloc()
B) calloc()
C) realloc()
D) resize()
Answer: C -
Where is dynamically allocated storage commonly
obtained from?
A) Heap
B) Register
C) Code segment
D) Preprocessor
Answer: A -
What should be checked after malloc()?
A) EOF
B) NULL
C) void
D) zero
Answer: B -
What is a memory leak?
A) Using too little memory
B) Losing access to allocated memory without releasing it
C) Reading a file
D) Using a static variable
Answer: B -
What should you do after free(ptr) if you want to
make ptr a safe null pointer?
A) ptr = NULL
B) ptr = 1
C) ptr++
D) realloc(ptr)
Answer: A -
Which function is declared in stdlib.h?
A) malloc()
B) printf()
C) strlen()
D) strcpy()
Answer: A -
Which is a dangerous operation?
A) free(ptr)
B) ptr = NULL
C) Dereferencing ptr after free(ptr)
D) malloc()
Answer: C
17.33 Practice Problems
Allocate memory dynamically for one integer and store a value in it.
Problem 2
Dynamically allocate an array of n integers.
Problem 3
Read n numbers into a dynamically allocated array and find their sum.
Problem 4
Find the largest element in a dynamically allocated array.
Problem 5
Find the smallest element in a dynamically allocated array.
Problem 6
Reverse a dynamically allocated array.
Problem 7
Use calloc() to create an array of integers and display its initial values.
Problem 8
Use realloc() to increase the size of an integer array.
Problem 9
Use realloc() to reduce the size of an array.
Problem 10
Dynamically allocate memory for a string and store a user-provided string.
Problem 11
Dynamically allocate a structure and access its members.
Problem 12
Dynamically allocate an array of structures.
Problem 13
Create a dynamically allocated 2D matrix.
Problem 14
Add two dynamically allocated matrices.
Problem 15
Find the transpose of a dynamically allocated matrix.
Problem 16
Demonstrate the difference between malloc() and calloc().
Problem 17
Write a program that safely handles malloc() failure.
Problem 18
Write a program demonstrating realloc() using a temporary pointer.
Problem 19
Demonstrate how a memory leak can occur and explain how to prevent it.
Problem 20
Create a dynamic Student Record Management System using an array of structures.
17.34 Key Takeaway
malloc() → Allocate memory
calloc() → Allocate + zero-initialize bytes
realloc() → Resize allocation
free() → Release memory
NULL → Check allocation failure
Heap → Common area for dynamic storage
Memory Leak → Allocated memory not released
Dangling Pointer → Pointer to expired/freed object
Use-After-Free → Accessing freed memory
💻 18. Command Line Arguments
18.1 What are Command Line Arguments?
Command line arguments are values supplied to a C program when the program is started from a command line or terminal.
They allow a program to receive input without using scanf() during execution.
↓
Program + Arguments
↓
main(argc, argv)
↓
Program Processing
Example:
program.exe 10 20 30
Here 10, 20 and 30 are command line arguments.
18.2 main() with Command Line Arguments
A common form of the main function is:
int main(int argc, char *argv[])
{
return 0;
}
The two parameters are:
- argc → argument count
- argv → argument vector, an array of pointers to the argument strings
18.3 argc - Argument Count
argc contains the number of command-line arguments, including the program name.
Example:
program.exe 10 20
The value of argc is:
Why 3?
10 → 1
20 → 1
Total = 3
18.4 argv - Argument Vector
argv is an array of strings containing the command-line arguments.
program.exe 10 20
18.5 Display Command Line Arguments
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("Argument count = %d\n\n", argc);
for(int i = 0; i < argc; i++)
{
printf("argv[%d] = %s\n", i, argv[i]);
}
return 0;
}
Suppose the program is executed as:
program.exe Hello 123 C
argv[0] = program.exe
argv[1] = Hello
argv[2] = 123
argv[3] = C
18.6 Important: Command Line Arguments are Strings
All command-line arguments are received as strings.
program.exe 10 20
Therefore:
argv[1]
contains the string:
"10"
It is not automatically an integer.
18.7 Converting Strings to Integers - atoi()
The atoi() function converts a string representing an integer into an int.
#include <stdlib.h>
int num;
num = atoi(argv[1]);
Example:
program.exe 25
int num = atoi(argv[1]);
Now:
18.8 Add Two Command Line Numbers
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
if(argc != 3)
{
printf("Usage: program number1 number2");
return 1;
}
int a = atoi(argv[1]);
int b = atoi(argv[2]);
printf("Sum = %d", a + b);
return 0;
}
Example:
program.exe 10 20
18.9 Subtract Two Numbers
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
if(argc != 3)
{
printf("Usage: program number1 number2");
return 1;
}
int a = atoi(argv[1]);
int b = atoi(argv[2]);
printf("Difference = %d", a - b);
return 0;
}
18.10 Multiply Two Numbers
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
if(argc != 3)
{
printf("Usage: program number1 number2");
return 1;
}
int a = atoi(argv[1]);
int b = atoi(argv[2]);
printf("Product = %d", a * b);
return 0;
}
18.11 Divide Two Numbers
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
if(argc != 3)
{
printf("Usage: program number1 number2");
return 1;
}
int a = atoi(argv[1]);
int b = atoi(argv[2]);
if(b == 0)
{
printf("Division by zero is not allowed");
return 1;
}
printf("Quotient = %d", a / b);
return 0;
}
18.12 Sum of Multiple Command Line Numbers
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int sum = 0;
for(int i = 1; i < argc; i++)
{
sum += atoi(argv[i]);
}
printf("Sum = %d", sum);
return 0;
}
Example:
program.exe 10 20 30 40
18.13 Find the Largest Command Line Number
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
if(argc < 2)
{
printf("Provide at least one number");
return 1;
}
int largest = atoi(argv[1]);
for(int i = 2; i < argc; i++)
{
int value = atoi(argv[i]);
if(value > largest)
{
largest = value;
}
}
printf("Largest = %d", largest);
return 0;
}
18.14 Find the Smallest Command Line Number
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
if(argc < 2)
{
printf("Provide at least one number");
return 1;
}
int smallest = atoi(argv[1]);
for(int i = 2; i < argc; i++)
{
int value = atoi(argv[i]);
if(value < smallest)
{
smallest = value;
}
}
printf("Smallest = %d", smallest);
return 0;
}
18.15 Average of Command Line Numbers
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
if(argc < 2)
{
printf("Provide at least one number");
return 1;
}
double sum = 0;
for(int i = 1; i < argc; i++)
{
sum += atoi(argv[i]);
}
double average = sum / (argc - 1);
printf("Average = %.2f", average);
return 0;
}
18.16 strtol() - Safer Integer Conversion
atoi() is simple, but it provides limited error reporting. For more robust programs, strtol() can be used.
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
int main(int argc, char *argv[])
{
if(argc != 2)
{
printf("Usage: program number");
return 1;
}
char *end;
long value;
errno = 0;
value = strtol(argv[1], &end, 10);
if(end == argv[1] || *end != '\0')
{
printf("Invalid integer");
return 1;
}
if(errno == ERANGE ||
value < INT_MIN ||
value > INT_MAX)
{
printf("Integer out of range");
return 1;
}
printf("Number = %ld", value);
return 0;
}
18.17 Passing a Character or Word
#include <stdio.h>
int main(int argc, char *argv[])
{
if(argc != 2)
{
printf("Usage: program name");
return 1;
}
printf("Hello %s!", argv[1]);
return 0;
}
Example:
program.exe Venu
18.18 Arguments Containing Spaces
If an argument contains spaces, the shell normally separates it into multiple arguments unless it is quoted.
program.exe "Venu Gopal"
Here:
argv[1]
18.19 Command Line Argument Diagram
18.20 Command Line Arguments vs scanf()
| scanf() | Command Line Arguments |
|---|---|
| Input is entered while program runs | Input is supplied when program starts |
| Reads typed input from standard input | Reads strings from argc/argv |
| Useful for interactive programs | Useful for scripts and command-line tools |
18.21 Checking the Number of Arguments
A program should check whether the required arguments have been provided before accessing them.
if(argc != 3)
{
printf("Usage: program number1 number2");
return 1;
}
18.22 Command Line Calculator
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
if(argc != 4)
{
printf("Usage: program number operator number");
return 1;
}
int a = atoi(argv[1]);
int b = atoi(argv[3]);
char op = argv[2][0];
switch(op)
{
case '+':
printf("Result = %d", a + b);
break;
case '-':
printf("Result = %d", a - b);
break;
case '*':
printf("Result = %d", a * b);
break;
case '/':
if(b == 0)
{
printf("Division by zero is not allowed");
return 1;
}
printf("Result = %d", a / b);
break;
default:
printf("Invalid operator");
}
return 0;
}
Example:
program.exe 10 + 20
18.23 Passing a File Name
Command-line arguments are often useful for supplying file names.
#include <stdio.h>
int main(int argc, char *argv[])
{
if(argc != 2)
{
printf("Usage: program filename");
return 1;
}
FILE *fp = fopen(argv[1], "r");
if(fp == NULL)
{
printf("Unable to open file");
return 1;
}
printf("File opened successfully");
fclose(fp);
return 0;
}
Example:
program.exe data.txt
18.24 Common Mistakes
- Forgetting that argv contains strings.
- Using argv[1] without checking argc.
- Performing arithmetic directly on strings.
- Forgetting to convert numeric arguments.
- Assuming the program name is not counted in argc.
- Using atoi() when detailed input validation is required.
- Forgetting that arguments containing spaces may need quoting in the command shell.
18.25 Quick Revision
📌 argv → Array of argument strings
📌 argv[0] → Usually the program name
📌 argv[1] → First user-supplied argument
📌 atoi() → Simple string-to-int conversion
📌 strtol() → More robust integer conversion
📌 argc & argv → Used to receive command-line input
18.26 Quick MCQs
-
What does argc represent?
A) Argument count
B) Argument character
C) Array count
D) Character count
Answer: A -
What does argv represent?
A) Argument vector
B) Argument value
C) Array variable
D) Automatic variable
Answer: A -
Which argument normally contains the program name?
A) argv[0]
B) argv[1]
C) argv[2]
D) argc
Answer: A -
If a program is run as:
program.exe 10 20
what is argc?
A) 2
B) 3
C) 10
D) 20
Answer: B -
What type of data does argv contain?
A) Integer values
B) Character strings
C) Floating-point values only
D) Structures
Answer: B -
Which function can convert a string to int?
A) atoi()
B) itoa()
C) stringtoint()
D) convert()
Answer: A -
Which function provides more robust integer conversion
and error handling?
A) atoi()
B) strtol()
C) printf()
D) scanf()
Answer: B -
Which header declares atoi() and strtol()?
A) stdio.h
B) stdlib.h
C) string.h
D) math.h
Answer: B -
Which should be checked before using argv[2]?
A) EOF
B) argc
C) NULL
D) sizeof
Answer: B -
Command line arguments are most directly useful for:
A) Supplying startup input to a program
B) Declaring variables
C) Creating loops
D) Defining structures
Answer: A
18.27 Practice Problems
Write a program to display all command-line arguments.
Problem 2
Display argc and every argv element with its index.
Problem 3
Write a program to print the first command-line argument.
Problem 4
Add two numbers supplied through command-line arguments.
Problem 5
Subtract two command-line numbers.
Problem 6
Multiply two command-line numbers.
Problem 7
Divide two command-line numbers safely.
Problem 8
Find the sum of n command-line numbers.
Problem 9
Find the average of n command-line numbers.
Problem 10
Find the largest command-line number.
Problem 11
Find the smallest command-line number.
Problem 12
Count how many command-line arguments are even numbers.
Problem 13
Count positive and negative command-line numbers.
Problem 14
Create a command-line calculator using +, -, * and /.
Problem 15
Accept a student's name and marks through command-line arguments and display them.
Problem 16
Accept a file name through the command line and open it.
Problem 17
Count the number of command-line arguments excluding the program name.
Problem 18
Find the longest command-line string.
Problem 19
Convert command-line numbers using strtol() and validate invalid input.
Problem 20
Create a command-line Student Result program that accepts a student's name and marks in three subjects and displays total, average and grade.
18.28 Key Takeaway
argc → How many arguments?
argv → What are the arguments?
argv[0] → Usually program name
argv[1] → First user argument
atoi() → Simple string → int
strtol() → Robust string → integer conversion
Always check argc before accessing argv elements.
🔢 19. Bitwise Programming
19.1 What is Bitwise Programming?
Bitwise programming operates directly on the individual bits of an integer.
A bit can have only two values: 0 or 1.
↓
Binary Representation
↓
Individual Bits
↓
Bitwise Operation
Bitwise operations are commonly used in low-level programming, embedded systems, networking, operating systems and programming interviews.
19.2 Binary Representation
Computers internally represent integer values using binary digits.
8-bit binary representation
For example:
10 = 00001010
19.3 Bitwise Operators
| Operator | Name | Purpose |
|---|---|---|
| & | Bitwise AND | Sets a bit to 1 only when both bits are 1 |
| | | Bitwise OR | Sets a bit to 1 when either bit is 1 |
| ^ | Bitwise XOR | Sets a bit to 1 when the bits are different |
| ~ | Bitwise NOT | Flips each bit |
| << | Left Shift | Shifts bits to the left |
| >> | Right Shift | Shifts bits to the right |
19.4 Bitwise AND (&)
The AND operation produces 1 only when both corresponding bits are 1.
int result = 5 & 3;
printf("%d", result);
19.5 AND Truth Table
| A | B | A & B |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 1 |
19.6 Bitwise OR (|)
OR produces 1 when at least one corresponding bit is 1.
int result = 5 | 3;
printf("%d", result);
19.7 Bitwise XOR (^)
XOR produces 1 when the corresponding bits are different.
int result = 5 ^ 3;
printf("%d", result);
19.8 XOR Truth Table
| A | B | A ^ B |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
19.9 Bitwise NOT (~)
The NOT operator flips every bit: 0 becomes 1 and 1 becomes 0.
int x = 5;
printf("%d", ~x);
For signed integers, the exact decimal result of ~x depends on the integer representation. On modern two's-complement systems, ~5 is commonly -6.
~x is commonly equal to -(x + 1) on two's-complement systems.
19.10 Left Shift (<<)
The left-shift operator moves bits toward the left.
int x = 5;
printf("%d", x << 1);
For a nonnegative value, shifting left by one position corresponds to multiplying by 2 when the result is representable.
19.11 Right Shift (>>)
The right-shift operator moves bits toward the right.
int x = 20;
printf("%d", x >> 2);
19.12 Important Shift Rules
- Shifting by a negative count is undefined behavior.
- Shifting by a count greater than or equal to the width of the promoted left operand is undefined behavior.
- Left-shifting signed values requires care because overflow can result in undefined behavior.
- Right-shifting a negative signed integer is implementation-defined.
- For predictable bit manipulation, unsigned integers are often preferable.
19.13 Check Odd or Even Using Bitwise AND
The least significant bit determines whether a nonnegative integer is odd or even.
if(n & 1)
{
printf("Odd");
}
else
{
printf("Even");
}
Example:
7 = 00000111
1 = 00000001
7 & 1 = 1
19.14 Check Whether a Bit is Set
To check bit position k, use:
if(n & (1u << k))
{
printf("Bit is set");
}
else
{
printf("Bit is not set");
}
Example: check bit 2 of 5.
5 = 00000101
1u << 2 = 00000100
5 & 4 = 4
19.15 Set a Bit
Setting a bit means changing it to 1.
n = n | (1u << k);
Short form:
n |= (1u << k);
Example:
n = 8; /* 1000 */
k = 1;
n |= (1u << k);
/* Result = 1010 = 10 */
19.16 Clear a Bit
Clearing a bit means changing it to 0.
n = n & ~(1u << k);
Short form:
n &= ~(1u << k);
19.17 Toggle a Bit
Toggling changes:
1 → 0
n ^= (1u << k);
19.18 Bit Mask
A bit mask is a value used to select or modify specific bits.
1u << k
creates a mask with bit k set.
19.19 Count Set Bits
A simple method is to examine every bit.
#include <stdio.h>
int main()
{
unsigned int n;
int count = 0;
scanf("%u", &n);
while(n != 0)
{
count += n & 1u;
n >>= 1;
}
printf("Set bits = %d", count);
return 0;
}
For example:
13 = 1101
19.20 Efficient Set Bit Counting
Brian Kernighan's method repeatedly removes the lowest set bit.
while(n != 0)
{
n = n & (n - 1);
count++;
}
This performs one iteration for each set bit.
19.21 Check Whether a Number is a Power of 2
A positive power of two has exactly one set bit.
if(n > 0 && (n & (n - 1)) == 0)
{
printf("Power of 2");
}
else
{
printf("Not a power of 2");
}
Examples:
2 = 0010
4 = 0100
8 = 1000
19.22 Clear the Lowest Set Bit
n = n & (n - 1);
This removes the lowest set bit from n.
n = 12;
/*
12 = 1100
11 = 1011
*/
n & (n - 1)
/*
1100
1011
----
1000
*/
Result = 8
19.23 Isolate the Lowest Set Bit
unsigned int lowest = n & (~n + 1);
An equivalent common expression is:
unsigned int lowest = n & -n;
For unsigned arithmetic, a useful portable formulation is to work with unsigned values when doing bit manipulation.
19.24 Swap Two Values Using XOR
XOR has the property:
A ^ 0 = A
a = a ^ b;
b = a ^ b;
a = a ^ b;
However, in normal C programs a temporary variable is usually clearer and safer.
19.25 Find the Unique Element Using XOR
If every number occurs exactly twice except one number, XOR can find the unique value.
int arr[] = {4, 1, 2, 1, 2};
int result = 0;
for(int i = 0; i < 5; i++)
{
result ^= arr[i];
}
printf("%d", result);
Because:
2 ^ 2 = 0
0 ^ 4 = 4
19.26 Using Bits as Flags
Individual bits can represent multiple yes/no settings inside one integer.
#define READ_PERMISSION (1u << 0)
#define WRITE_PERMISSION (1u << 1)
#define EXEC_PERMISSION (1u << 2)
unsigned int permissions = 0;
permissions |= READ_PERMISSION;
permissions |= WRITE_PERMISSION;
Now multiple independent flags are stored in one value.
19.27 Set, Clear and Toggle a Bit
#include <stdio.h>
int main()
{
unsigned int n;
int k;
scanf("%u %d", &n, &k);
/* Set */
n |= (1u << k);
printf("After set: %u\n", n);
/* Clear */
n &= ~(1u << k);
printf("After clear: %u\n", n);
/* Toggle */
n ^= (1u << k);
printf("After toggle: %u\n", n);
return 0;
}
19.28 Print Binary Representation
#include <stdio.h>
void printBinary(unsigned int n)
{
unsigned int mask = 1u <<
(sizeof(unsigned int) * 8 - 1);
while(mask != 0)
{
printf("%d", (n & mask) != 0);
mask >>= 1;
}
}
int main()
{
unsigned int n;
scanf("%u", &n);
printBinary(n);
return 0;
}
19.29 Bitwise vs Logical Operators
| Bitwise | Logical |
|---|---|
| & | && |
| | | || |
| ^ | No logical XOR operator in C |
| Works on individual bits | Works with truth values |
19.30 Common Bitwise Mistakes
- Confusing & with &&.
- Confusing | with ||.
- Forgetting operator precedence.
- Using signed integers carelessly for shifts.
- Shifting by an invalid amount.
- Assuming right shift of negative signed values is always the same on every implementation.
- Forgetting to use parentheses in complex expressions.
19.31 Important Operator Precedence
When writing bitwise expressions, parentheses improve readability and reduce mistakes.
if((n & (1u << k)) != 0)
{
printf("Set");
}
Prefer this explicit form rather than relying on remembering every precedence rule.
19.32 Quick Revision
📌 | → OR
📌 ^ → XOR
📌 ~ → NOT
📌 << → Left shift
📌 >> → Right shift
📌 n & 1 → Check least significant bit
📌 n | (1u << k) → Set bit
📌 n & ~(1u << k) → Clear bit
📌 n ^ (1u << k) → Toggle bit
📌 n & (n - 1) → Remove lowest set bit
📌 n & (n - 1) == 0 → Power-of-two test when n is positive
19.33 Quick MCQs
-
Which operator performs bitwise AND?
A) &
B) &&
C) |
D) ||
Answer: A -
Which operator performs bitwise XOR?
A) &
B) ^
C) ||
D) ~
Answer: B -
Which operator flips bits?
A) &
B) |
C) ^
D) ~
Answer: D -
What is 5 & 3?
A) 1
B) 2
C) 7
D) 8
Answer: A -
What is 5 | 3?
A) 1
B) 6
C) 7
D) 8
Answer: C -
What is 5 ^ 3?
A) 1
B) 6
C) 7
D) 8
Answer: B -
Which operator performs left shift?
A) <<
B) >>
C) &
D) ^
Answer: A -
Which expression checks whether bit k is set?
A) n & (1u << k)
B) n | k
C) n ^ k
D) n + k
Answer: A -
Which expression sets bit k?
A) n &= (1u << k)
B) n |= (1u << k)
C) n ^= ~(1u << k)
D) n >>= k
Answer: B -
Which expression removes the lowest set bit?
A) n | (n - 1)
B) n ^ (n - 1)
C) n & (n - 1)
D) n + (n - 1)
Answer: C
19.34 Practice Problems
Find the result of bitwise AND for two integers.
Problem 2
Find the result of bitwise OR for two integers.
Problem 3
Find the result of bitwise XOR for two integers.
Problem 4
Find the bitwise NOT of an integer.
Problem 5
Perform left shift by one position.
Problem 6
Perform right shift by two positions.
Problem 7
Check whether a number is odd or even using bitwise AND.
Problem 8
Check whether the kth bit is set.
Problem 9
Set the kth bit.
Problem 10
Clear the kth bit.
Problem 11
Toggle the kth bit.
Problem 12
Count the number of set bits in an integer.
Problem 13
Count set bits using Brian Kernighan's algorithm.
Problem 14
Check whether a positive integer is a power of 2.
Problem 15
Find the unique element in an array where every other element appears exactly twice.
Problem 16
Print the binary representation of an unsigned integer.
Problem 17
Swap two integers using XOR.
Problem 18
Implement a program to set, clear and toggle a selected bit.
Problem 19
Implement a permission system using bit flags.
Problem 20
Given an array in which every number appears twice except two numbers, find the two numbers using bitwise operations.
19.35 Key Takeaway
AND (&) → Select bits
OR (|) → Set bits
XOR (^) → Toggle / find differences
NOT (~) → Flip bits
Left Shift (<<) → Move bits left
Right Shift (>>) → Move bits right
n & 1 → Check odd/even
n & (n - 1) → Remove lowest set bit
1u << k → Create a mask for bit k
🔤 20. Enumerations (enum) and typedef
20.1 What is an enum?
An enum (enumeration) is a user-defined type in C that gives meaningful names to a set of integer constants.
It improves the readability of programs when a variable can have one value from a fixed set of choices.
enum Day
{
SUNDAY,
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY
};
By default, the first enumerator has value 0, the next has value 1, and so on.
20.2 Default enum Values
| Enumerator | Default Value |
|---|---|
| SUNDAY | 0 |
| MONDAY | 1 |
| TUESDAY | 2 |
| WEDNESDAY | 3 |
| THURSDAY | 4 |
| FRIDAY | 5 |
| SATURDAY | 6 |
20.3 Declaring an enum Variable
enum Day today;
today = MONDAY;
A variable declared as enum Day can store an enumeration value.
#include <stdio.h>
enum Day
{
SUNDAY,
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY
};
int main()
{
enum Day today = MONDAY;
printf("%d", today);
return 0;
}
20.4 Custom enum Values
You can explicitly assign values to enumerators.
enum Status
{
SUCCESS = 1,
FAILURE = 0
};
Example:
#include <stdio.h>
enum Status
{
FAILURE = 0,
SUCCESS = 1
};
int main()
{
enum Status result = SUCCESS;
printf("%d", result);
return 0;
}
20.5 Mixed enum Values
When one enumerator is explicitly assigned a value, subsequent enumerators continue from that value.
enum Numbers
{
A = 10,
B,
C,
D = 20,
E
};
| Enumerator | Value |
|---|---|
| A | 10 |
| B | 11 |
| C | 12 |
| D | 20 |
| E | 21 |
20.6 enum with switch
Enums are especially useful with switch statements.
#include <stdio.h>
enum Day
{
SUNDAY,
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY
};
int main()
{
enum Day day = WEDNESDAY;
switch(day)
{
case SUNDAY:
printf("Sunday");
break;
case MONDAY:
printf("Monday");
break;
case TUESDAY:
printf("Tuesday");
break;
case WEDNESDAY:
printf("Wednesday");
break;
case THURSDAY:
printf("Thursday");
break;
case FRIDAY:
printf("Friday");
break;
case SATURDAY:
printf("Saturday");
break;
}
return 0;
}
20.7 Advantages of enum
- Makes code easier to read.
- Provides meaningful names for integer constants.
- Useful when a variable has a fixed set of logical choices.
- Works naturally with switch statements.
- Reduces the use of unexplained numeric constants.
20.8 Real-Life Example: Traffic Light
#include <stdio.h>
enum TrafficLight
{
RED,
YELLOW,
GREEN
};
int main()
{
enum TrafficLight light = GREEN;
if(light == RED)
{
printf("STOP");
}
else if(light == YELLOW)
{
printf("READY");
}
else
{
printf("GO");
}
return 0;
}
20.9 Size of an enum
The size of an enum is implementation-defined. It is commonly the size of an integer type, but portable C code should not assume a specific size.
#include <stdio.h>
enum Color
{
RED,
GREEN,
BLUE
};
int main()
{
printf("%zu", sizeof(enum Color));
return 0;
}
20.10 Important Point About enum
📌 The exact underlying representation of an enum is implementation-defined.
📌 Do not assume every enum object must occupy exactly 4 bytes.
20.11 What is typedef?
typedef creates an alternative name (alias) for an existing type.
It does not create a completely new type.
typedef int Number;
Now both of the following declare an integer:
int a;
Number b;
20.12 Basic typedef Example
#include <stdio.h>
typedef int Number;
int main()
{
Number x = 100;
printf("%d", x);
return 0;
}
20.13 typedef with Multiple Variables
typedef unsigned int UINT;
UINT a = 10;
UINT b = 20;
This can make declarations shorter and easier to read.
20.14 typedef with Structure
Without typedef:
struct Student
{
int id;
char name[50];
};
struct Student s1;
With typedef:
typedef struct
{
int id;
char name[50];
} Student;
Student s1;
This is one of the most common uses of typedef in C.
20.15 Complete typedef Structure Example
#include <stdio.h>
typedef struct
{
int id;
char name[50];
float marks;
} Student;
int main()
{
Student s;
s.id = 101;
s.marks = 85.5;
printf("ID = %d\n", s.id);
printf("Marks = %.2f\n", s.marks);
return 0;
}
Marks = 85.50
20.16 typedef with enum
typedef can also be used with enum.
typedef enum
{
LOW,
MEDIUM,
HIGH
} Level;
Now you can simply write:
Level current = HIGH;
20.17 typedef with Pointer
typedef int* IntPtr;
int x = 10;
IntPtr p = &x;
Here IntPtr is an alias for int *.
20.18 Important typedef Pointer Example
typedef int* IntPtr;
IntPtr p1, p2;
Both p1 and p2 are pointers to int.
Compare this with:
int *p1, p2;
Here only p1 is a pointer. p2 is an ordinary int.
20.19 typedef with Array
typedef int Marks[5];
Marks studentMarks;
studentMarks[0] = 90;
Here Marks represents an array of five ints.
20.20 typedef with Function Pointer
Function pointers can be difficult to read. typedef can make them simpler.
typedef int (*Operation)(int, int);
Now:
Operation op;
can represent a pointer to a function taking two ints and returning an int.
20.21 Complete Function Pointer Example
#include <stdio.h>
typedef int (*Operation)(int, int);
int add(int a, int b)
{
return a + b;
}
int main()
{
Operation op = add;
printf("%d", op(10, 20));
return 0;
}
20.22 typedef vs #define
| typedef | #define |
|---|---|
| Creates a type alias | Preprocessor text substitution |
| Understood by the compiler | Processed before compilation |
| Useful for types | Useful for macros and constants |
| Can make complex declarations easier | Performs textual replacement |
20.23 typedef Does Not Create a Variable
typedef int Number;
This does not create a variable.
It creates an alias named Number.
Number x = 10;
This statement creates the actual variable.
20.24 enum vs typedef
| enum | typedef |
|---|---|
| Defines named integer constants | Creates an alias for an existing type |
| Useful for a fixed set of choices | Useful for simplifying type declarations |
| Example: RED, GREEN, BLUE | Example: typedef int Number; |
20.25 Using enum and typedef Together
typedef enum
{
PENDING,
APPROVED,
REJECTED
} Status;
typedef struct
{
int id;
Status status;
} Application;
Now an application can have a meaningful status.
Application app;
app.id = 1001;
app.status = APPROVED;
20.26 Real-Life Student Example
#include <stdio.h>
typedef enum
{
FAIL,
PASS
} Result;
typedef struct
{
int id;
char name[50];
float marks;
Result result;
} Student;
int main()
{
Student s = {101, "Venu", 78.5, PASS};
printf("ID = %d\n", s.id);
printf("Name = %s\n", s.name);
printf("Marks = %.2f\n", s.marks);
if(s.result == PASS)
printf("Result = PASS");
else
printf("Result = FAIL");
return 0;
}
Name = Venu
Marks = 78.50
Result = PASS
20.27 Common Mistakes
- Assuming enum always occupies exactly 4 bytes.
- Thinking typedef creates a completely new type.
- Confusing typedef with #define.
- Forgetting the semicolon after an enum or typedef declaration.
- Confusing an enum type with its individual enumerator names.
- Using confusing typedef names that hide pointer or array behavior.
20.28 Quick Revision
📌 First enum value is normally 0
📌 Enum values can be explicitly assigned
📌 typedef → Creates a type alias
📌 typedef does not create a new variable
📌 typedef is commonly used with structures
📌 typedef can simplify pointer declarations
📌 typedef can be used with enum, arrays and function pointers
📌 #define performs preprocessor text substitution
20.29 Quick MCQs
-
What is enum used for?
A) Named integer constants
B) Dynamic memory
C) File handling
D) Pointer arithmetic
Answer: A -
What is the default value of the first enum constant?
A) 0
B) 1
C) -1
D) Undefined
Answer: A -
Which keyword creates a type alias?
A) alias
B) typedef
C) type
D) define
Answer: B -
Does typedef create a new variable?
A) Yes
B) No
C) Only for pointers
D) Only for structures
Answer: B -
Which is commonly used with typedef?
A) Structure
B) goto
C) switch only
D) printf
Answer: A -
Which performs preprocessor text substitution?
A) typedef
B) #define
C) enum
D) struct
Answer: B -
Which declaration creates an alias for int?
A) alias int Number;
B) typedef int Number;
C) define int Number;
D) enum int Number;
Answer: B -
Which is a valid typedef pointer declaration?
A) typedef int* IntPtr;
B) pointer int IntPtr;
C) typedef pointer int;
D) int typedef* IntPtr;
Answer: A -
Can typedef be used with a function pointer?
A) Yes
B) No
C) Only in C++
D) Only with void functions
Answer: A -
Which is the best reason to use enum?
A) To make fixed choices readable
B) To allocate memory dynamically
C) To open files
D) To create threads
Answer: A
20.30 Practice Problems
Create an enum representing the seven days of the week.
Problem 2
Print the integer value of each day in an enum.
Problem 3
Create an enum for RED, YELLOW and GREEN traffic lights.
Problem 4
Use an enum with switch to display the day of the week.
Problem 5
Create an enum with custom values 10, 20 and 30.
Problem 6
Create an enum for student result: FAIL, PASS and DISTINCTION.
Problem 7
Create an enum representing user roles: ADMIN, TEACHER and STUDENT.
Problem 8
Use typedef to create an alias for int.
Problem 9
Use typedef to create an alias for unsigned int.
Problem 10
Create a typedef structure for Student.
Problem 11
Create a typedef structure for Employee.
Problem 12
Create a typedef for an integer pointer.
Problem 13
Create a typedef for an array of 10 integers.
Problem 14
Create a typedef for a function pointer that adds two integers.
Problem 15
Create a Student structure containing an enum Result field.
Problem 16
Create a typedef enum representing account status: ACTIVE, BLOCKED and CLOSED.
Problem 17
Create a typedef structure for a bank account and use an enum for account status.
Problem 18
Create a typedef function pointer for multiplication and division operations.
Problem 19
Create a Student Management structure using typedef, enum and an array.
Problem 20
Create a complete Employee Management program using typedef struct and enum for employee department and status.
20.31 Key Takeaway
enum → Give names to related integer constants.
typedef → Give an existing type a convenient alias.
enum + switch → Excellent combination for fixed choices.
typedef + struct → Cleaner structure declarations.
typedef + pointer → Can simplify pointer declarations.
typedef + function pointer → Makes complex declarations easier.
#define ≠ typedef.
🧮 21. Operator Precedence & Associativity
21.1 What is Operator Precedence?
Operator precedence determines which operator is evaluated first when an expression contains multiple operators.
For example:
int result = 10 + 5 * 2;
Multiplication has higher precedence than addition. Therefore:
10 + 10 = 20
21.2 What is Associativity?
When two operators have the same precedence, associativity determines the direction in which they are evaluated.
Most arithmetic, relational and logical operators have left-to-right associativity.
Unary, conditional and assignment operators contain important right-to-left cases.
20 / 5 * 2
Division and multiplication have the same precedence, so they are evaluated from left to right:
4 × 2 = 8
21.3 C Operator Precedence Table
| Priority | Operators | Associativity |
|---|---|---|
| Highest | () [] -> . postfix ++ postfix -- | Left → Right |
| 2 | ++ -- + - ! ~ (type) * & | Right → Left |
| 3 | * / % | Left → Right |
| 4 | + - | Left → Right |
| 5 | << >> | Left → Right |
| 6 | < <= > >= | Left → Right |
| 7 | == != | Left → Right |
| 8 | & | Left → Right |
| 9 | ^ | Left → Right |
| 10 | | | Left → Right |
| 11 | && | Left → Right |
| 12 | || | Left → Right |
| 13 | ?: | Right → Left |
| 14 | = += -= *= /= %= <<= >>= &= ^= |= | Right → Left |
| Lowest | , | Left → Right |
📌 Parentheses can be used to explicitly control evaluation order.
21.4 Parentheses Have Highest Practical Priority
Parentheses can change the order of evaluation.
10 + 5 * 2
(10 + 5) * 2
Therefore, use parentheses when the intended meaning needs to be made explicit.
21.5 Arithmetic Operator Precedence
Multiplication, division and modulus have higher precedence than addition and subtraction.
int x = 10 + 20 * 3;
10 + 60 = 70
21.6 Modulus with Other Operators
int x = 20 + 15 % 4;
20 + 3 = 23
21.7 Left-to-Right Associativity
100 / 10 * 2
Both / and * have the same precedence and associate left-to-right.
10 × 2 = 20
21.8 Subtraction Example
20 - 5 - 3
The - operator associates from left to right.
15 - 3 = 12
21.9 Relational Operators
Relational operators have lower precedence than arithmetic operators.
int result = 10 + 5 > 12;
15 > 12
true → 1
21.10 Equality vs Relational Operators
Relational operators have higher precedence than equality operators.
int result = 5 < 10 == 1;
1 == 1 → 1
21.11 Logical Operator Precedence
The order among the common logical operators is:
↓
&&
↓
||
1 || 0 && 0
First evaluate AND:
1 || 0 = 1
21.12 Logical NOT
Logical NOT has higher precedence than logical AND.
!0 && 1
1 && 1 = 1
21.13 Bitwise Operator Precedence
Among the bitwise operators:
↓
^
↓
|
Bitwise AND has higher precedence than XOR, which has higher precedence than OR.
5 | 3 & 1
First:
3 & 1 = 1
Then:
5 | 1 = 5
21.14 Shift vs Arithmetic Operators
Shift operators have lower precedence than addition and subtraction.
1 << 2 + 1
Addition is evaluated first:
1 << 3 = 8
21.15 Assignment Operator
Assignment operators have lower precedence than most operators.
int x;
x = 10 + 20 * 2;
First multiplication, then addition, then assignment.
10 + 40 = 50
x = 50
21.16 Assignment is Right-to-Left
int a, b, c;
a = b = c = 10;
Assignment associates from right to left:
b = c
a = b
Therefore all three variables become 10.
21.17 Compound Assignment
x += 5;
is equivalent in value effect to:
x = x + 5;
Similar operators include:
+=
-=
*=
/=
%=
&=
|=
^=
<<=
>>=
21.18 Pre-increment vs Post-increment
int x = 5;
int a = ++x;
First x is incremented, then its value is used.
a = 6
Post-increment:
int x = 5;
int a = x++;
x = 6
21.19 Pre-decrement vs Post-decrement
int x = 5;
int a = --x;
a = 4
int x = 5;
int a = x--;
x = 4
21.20 Function Call and Array Subscript
Function calls, array subscripting and structure member access bind very tightly.
arr[i]
function(x)
student.name
These operators appear near the highest level of the precedence hierarchy.
21.21 Pointer Dereference and Increment
Be careful with:
*p++
This is interpreted as:
*(p++)
It does not mean:
(*p)++
21.22 Pointer Expression Example
int arr[] = {10, 20, 30};
int *p = arr;
printf("%d", *p++);
Because postfix ++ has higher precedence than unary *, this is equivalent to:
*(p++)
The value printed is 10, and then p moves to the next array element.
21.23 Conditional Operator
The conditional operator has the form:
condition ? expression1 : expression2
Example:
int max = a > b ? a : b;
It is useful for simple conditional expressions.
21.24 Conditional Operator Associativity
The conditional operator associates from right to left.
a ? b : c ? d : e
It is interpreted as:
a ? b : (c ? d : e)
21.25 Comma Operator
The comma operator has the lowest precedence among the standard C operators.
int x;
x = (10, 20, 30);
The expressions are evaluated from left to right and the value of the last expression is the result.
21.26 Tricky Expression 1
int x = 10 + 20 * 3;
10 + 60 = 70
21.27 Tricky Expression 2
int x = 20 / 5 * 2;
4 × 2 = 8
21.28 Tricky Expression 3
int x = 10 + 5 > 12;
15 > 12 = 1
21.29 Tricky Expression 4
int x = 5 | 3 & 1;
5 | 1 = 5
21.30 Tricky Expression 5
int x = 1 << 2 + 1;
1 << 3 = 8
21.31 Tricky Expression 6
int a = 5;
int b = 10;
int result = a < b && b < 20;
10 < 20 → 1
1 && 1 → 1
21.32 Tricky Expression 7
int a = 5;
int b = 10;
int result = a + b > 10 && b > 5;
15 > 10 → 1
10 > 5 → 1
1 && 1 → 1
21.33 Precedence Does NOT Determine Evaluation Order
Operator precedence determines how an expression is grouped, but it does not generally tell you the order in which independent operands are evaluated.
For example, avoid writing expressions where the same scalar object is modified more than once without the required sequencing.
21.34 Dangerous Expressions
Avoid expressions such as:
i = i++ + ++i;
The problem is not simply precedence. The expression modifies i multiple times without the required sequencing between those modifications.
Such expressions can result in undefined behavior and should not be used.
21.35 Write Clear Code Instead
Instead of writing a complicated expression:
i = i++ + ++i;
use separate statements:
i++;
i++;
result = i;
21.36 Best Practice: Use Parentheses
if((a & mask) == 0)
{
printf("Bit is clear");
}
This is much clearer than relying on the reader to remember the exact precedence relationship between & and ==.
21.37 Common Mistakes
- Assuming expressions are always evaluated strictly from left to right.
- Confusing precedence with evaluation order.
- Forgetting that * / % have higher precedence than + -.
- Confusing bitwise operators with logical operators.
- Forgetting that assignment associates right-to-left.
- Misreading *p++ as (*p)++.
- Writing complicated expressions without parentheses.
- Modifying the same scalar object multiple times without the required sequencing.
21.38 Quick Revision
📌 Associativity → Resolves operators of the same precedence.
📌 * / % → Higher than + -
📌 + - → Higher than relational operators
📌 < <= > >= → Higher than == !=
📌 == != → Higher than bitwise AND
📌 & → Higher than ^
📌 ^ → Higher than |
📌 | → Higher than &&
📌 && → Higher than ||
📌 Assignment → Right-to-left
📌 Comma → Lowest precedence
📌 Parentheses → Use them to make intended grouping explicit.
21.39 Quick MCQs
-
Which operator has higher precedence?
A) +
B) *
C) =
D) ||
Answer: B -
What is the associativity of multiplication?
A) Left-to-right
B) Right-to-left
C) Top-to-bottom
D) None
Answer: A -
Which has higher precedence?
A) ==
B) <
C) =
D) ||
Answer: B -
What is the result of 10 + 5 * 2?
A) 30
B) 20
C) 25
D) 15
Answer: B -
What is the result of 20 / 5 * 2?
A) 2
B) 8
C) 10
D) 20
Answer: B -
Which operator has lower precedence than ==?
A) +
B) *
C) &&
D) <
Answer: C -
Which operator associates right-to-left?
A) +
B) *
C) =
D) %
Answer: C -
What is the result of 1 << 2 + 1?
A) 5
B) 6
C) 8
D) 9
Answer: C -
What is *p++ interpreted as?
A) (*p)++
B) *(p++)
C) *(++p)
D) *p + 1
Answer: B -
Which operator has the lowest precedence?
A) +
B) &&
C) =
D) comma
Answer: D
21.40 Practice Problems
Find the output of:
10 + 5 * 2
Problem 2
Find the output of:
20 / 5 * 2
Problem 3
Evaluate:
10 + 5 > 12
Problem 4
Evaluate:
5 | 3 & 1
Problem 5
Evaluate:
1 << 2 + 1
Problem 6
Determine the result of:
1 || 0 && 0
Problem 7
Determine the values of a and b:
a = b = 20
Problem 8
Find the output of a pre-increment expression.
Problem 9
Find the output of a post-increment expression.
Problem 10
Explain the difference between
*p++ and
(*p)++.
Problem 11
Evaluate a nested conditional operator expression.
Problem 12
Find the result of a comma operator expression.
Problem 13
Identify the precedence order in:
a + b * c - d.
Problem 14
Identify the precedence order in:
a && b || c.
Problem 15
Evaluate:
10 > 5 == 1.
Problem 16
Evaluate:
5 & 3 == 1
and explain why parentheses improve clarity.
Problem 17
Rewrite a complicated expression using parentheses to make its evaluation order clear.
Problem 18
Explain why precedence does not determine the complete evaluation order of an expression.
Problem 19
Identify whether a given expression has well-defined behavior when a variable is modified multiple times.
Problem 20
Create a program containing ten tricky C expressions, evaluate them and explain each result.
21.41 Key Takeaway
Precedence = grouping priority.
Associativity = direction for equal-precedence operators.
*, /, % come before + and -.
Arithmetic operators come before relational operators.
Relational operators come before equality operators.
Bitwise AND comes before XOR, which comes before OR.
&& comes before ||.
Assignment associates right-to-left.
Postfix operators such as p++ bind very tightly.
Precedence is not the same as evaluation order.
Use parentheses when an expression may be difficult to understand.
💾 22. Storage Classes in C
22.1 What is a Storage Class?
A storage class in C specifies important properties of a variable, such as its scope, lifetime, visibility and storage behavior.
The commonly discussed storage-class specifiers in C are:
- auto
- register
- static
- extern
22.2 Main Properties
| Property | Meaning |
|---|---|
| Scope | Where the variable can be accessed |
| Lifetime | How long the variable exists |
| Linkage | Whether the same name can refer to the same entity across scopes/files |
| Storage | How the implementation manages storage for the object |
22.3 auto Storage Class
auto is the default storage-class specifier for local variables declared inside a block.
int main()
{
auto int x = 10;
printf("%d", x);
return 0;
}
In normal C programming, the auto keyword is
rarely written explicitly because local variables are
automatic by default.
int x = 10;
is normally equivalent in storage-class behavior to:
auto int x = 10;
22.4 Scope of auto Variables
#include <stdio.h>
int main()
{
int x = 10;
{
int y = 20;
printf("%d %d\n", x, y);
}
printf("%d", x);
return 0;
}
10
The variable y exists only within its block.
22.5 register Storage Class
The register keyword requests that the implementation consider keeping a variable in a processor register for potentially faster access.
register int count;
Modern compilers generally make their own optimization
decisions, so explicitly using register is
rarely necessary.
22.6 Example of register
#include <stdio.h>
int main()
{
register int i;
for(i = 0; i < 5; i++)
{
printf("%d ", i);
}
return 0;
}
22.7 Important Point About register
register is a request to the implementation,
not a guarantee that the variable will actually be stored
in a CPU register.
📌 You cannot apply the address-of operator
& to a variable declared with
register.
register int x = 10;
/* &x is not allowed */
22.8 static Storage Class
The static keyword has different effects depending on where it is used.
For a local variable, static preserves its value between function calls.
void counter()
{
static int count = 0;
count++;
printf("%d\n", count);
}
22.9 Static Local Variable
#include <stdio.h>
void counter()
{
static int count = 0;
count++;
printf("%d\n", count);
}
int main()
{
counter();
counter();
counter();
return 0;
}
2
3
Unlike an ordinary automatic local variable, the static local variable retains its stored value between calls.
22.10 Static Local Variable: Important Concept
Second call → count = 2
Third call → count = 3
The variable has block scope, but its lifetime extends for the entire execution of the program.
22.11 Static Local vs Automatic Local
| Feature | Automatic Local | Static Local |
|---|---|---|
| Scope | Block | Block |
| Lifetime | During execution of the block/function invocation | Entire program execution |
| Retains value? | No | Yes |
| Default initialization | Indeterminate if not initialized | Zero-initialized |
22.12 Static Global Variable
A file-scope variable declared with static
has internal linkage.
static int total = 100;
Such a variable can be accessed by functions in the same source file, but it is not available for external linkage from another source file through that identifier.
22.13 Why Use a Static Global Variable?
It is useful when a file needs a private global variable that should not be accessible through external linkage from other source files.
static int fileCounter = 0;
22.14 extern Storage Class
The extern keyword declares an object or function that has linkage to a definition elsewhere.
It is commonly used when sharing a global variable between different source files.
extern int total;
This declaration does not itself provide a definition with
storage for total.
22.15 extern Example
File 1: main.c
#include <stdio.h>
extern int total;
int main()
{
printf("%d", total);
return 0;
}
File 2: data.c
int total = 100;
When both files are compiled and linked together, the
declaration in main.c refers to the definition
in data.c.
22.16 extern Does Not Create a Second Variable
int total = 100;
This is the definition.
extern int total;
This is a declaration referring to an object with compatible linkage and type.
22.17 Global Variable Without static
int total = 100;
A file-scope variable without static normally
has external linkage, unless another declaration changes
the linkage rules.
22.18 Four Storage-Class Keywords
| Keyword | Typical Use | Important Property |
|---|---|---|
| auto | Local variables | Automatic storage duration |
| register | Local variables | Requests register-based optimization |
| static | Local/file-scope variables | Static storage duration; file-scope static has internal linkage |
| extern | Referencing external definitions | Declares an entity with linkage defined elsewhere |
22.19 Scope vs Lifetime
These two concepts are different.
| Concept | Meaning |
|---|---|
| Scope | Where a name can be used in the source code |
| Lifetime | How long the object exists during program execution |
22.20 Example: Scope vs Lifetime
void test()
{
static int x = 10;
printf("%d", x);
}
The name x has block scope, but the object
exists throughout program execution.
22.21 Static Variable Initialization
Objects with static storage duration are initialized before program startup. If no initializer is provided, they are initialized to zero (or the appropriate null value for pointer types).
static int x;
Here x is initialized to:
22.22 Automatic Variable Initialization
An automatic local variable that is declared without an initializer has an indeterminate value. Reading such a value before assigning a valid value leads to undefined behavior.
void test()
{
int x;
/* Do not read x before assigning a value */
}
22.23 Static Counter Example
#include <stdio.h>
void visit()
{
static int count = 0;
count++;
printf("Visited %d times\n", count);
}
int main()
{
visit();
visit();
visit();
visit();
return 0;
}
Visited 2 times
Visited 3 times
Visited 4 times
22.24 Static and Recursive Functions
A static local variable can be useful in recursive functions when persistent state is intentionally required.
#include <stdio.h>
void countCalls()
{
static int count = 0;
count++;
printf("%d ", count);
}
int main()
{
countCalls();
countCalls();
countCalls();
return 0;
}
22.25 Static Function
The static keyword can also be applied to a
function definition at file scope.
static void display()
{
printf("Hello");
}
Such a function has internal linkage and cannot be referred to by name from another translation unit.
22.26 Internal vs External Linkage
| Linkage | Meaning |
|---|---|
| Internal linkage | Name is limited to the current translation unit |
| External linkage | Name can refer to the same entity across translation units |
| No linkage | Name does not refer to an entity outside its scope |
22.27 Storage Duration
C also defines the concept of storage duration.
| Storage Duration | Typical Example |
|---|---|
| Automatic | Ordinary local variables |
| Static | Global variables and static local variables |
| Allocated | Memory obtained using malloc/calloc/realloc |
| Thread | Thread-local objects using _Thread_local |
22.28 Important Difference: static Local vs static Global
| static Local | static File-Scope |
|---|---|
| Block scope | File scope |
| Retains value between function calls | Exists throughout program execution |
| Static storage duration | Static storage duration |
| Does not have linkage | Internal linkage |
22.29 When Should You Use static?
- When a local variable must retain its value between function calls.
- When a file-scope variable should be private to its source file.
- When a helper function should have internal linkage.
22.30 When Should You Use extern?
- When referring to a global object defined elsewhere.
- When organizing a large C project into multiple source files.
- When sharing declarations through header files.
22.31 Example with Header File
global.h
extern int total;
global.c
int total = 500;
main.c
#include <stdio.h>
#include "global.h"
int main()
{
printf("%d", total);
return 0;
}
22.32 Common Mistakes
-
Thinking
staticalways means "global". -
Thinking
registerguarantees CPU-register storage. - Reading an uninitialized automatic local variable.
-
Thinking
externcreates a new variable. - Confusing scope with lifetime.
-
Forgetting that file-scope
staticgives internal linkage. -
Assuming an
externdeclaration is itself a definition.
22.33 Quick Revision
📌 register → Requests register-oriented optimization; compiler may ignore the request.
📌 static local → Retains value between function calls.
📌 static file-scope → Internal linkage.
📌 extern → Declares an object/function defined with appropriate linkage elsewhere.
📌 Scope → Where the name can be used.
📌 Lifetime → How long the object exists.
📌 Linkage → Whether declarations in different scopes/files can refer to the same entity.
22.34 Quick MCQs
-
Which storage class is the default for ordinary local
variables?
A) static
B) auto
C) extern
D) register
Answer: B -
Which keyword allows a local variable to retain its
value between function calls?
A) auto
B) register
C) static
D) extern
Answer: C -
Which keyword declares a reference to an object defined
elsewhere?
A) auto
B) static
C) register
D) extern
Answer: D -
Which keyword can be used for a file-scope function to
give it internal linkage?
A) auto
B) static
C) register
D) extern
Answer: B -
An uninitialized static-duration int object is initialized
to:
A) 1
B) -1
C) 0
D) Garbage
Answer: C -
Which keyword does not guarantee that a variable will be
stored in a CPU register?
A) register
B) static
C) extern
D) auto
Answer: A -
A static local variable has:
A) Block scope and static storage duration
B) File scope only
C) No scope
D) Automatic storage duration
Answer: A -
Which concept describes how long an object exists?
A) Scope
B) Lifetime
C) Syntax
D) Precedence
Answer: B -
File-scope static variables generally have:
A) External linkage
B) Internal linkage
C) No storage
D) Dynamic linkage
Answer: B -
Does an extern declaration necessarily define a new
object?
A) Yes
B) No
C) Only for integers
D) Only inside functions
Answer: B
22.35 Practice Problems
Explain the purpose of the auto storage class.
Problem 2
Write a program using an automatic local variable.
Problem 3
Explain the purpose of register.
Problem 4
Write a program containing a register loop counter.
Problem 5
Explain why register does not guarantee CPU-register allocation.
Problem 6
Write a function using a static local variable.
Problem 7
Write a program that counts how many times a function is called using static.
Problem 8
Explain the difference between an automatic local variable and a static local variable.
Problem 9
Create a file-scope static variable.
Problem 10
Explain internal linkage.
Problem 11
Explain the purpose of extern.
Problem 12
Create two C files and share a global variable using extern.
Problem 13
Explain the difference between scope and lifetime.
Problem 14
Predict the output of a program containing a static counter function.
Problem 15
Identify whether a variable has automatic or static storage duration.
Problem 16
Explain why reading an uninitialized automatic variable is unsafe.
Problem 17
Explain the difference between static global and static local variables.
Problem 18
Create a header file containing an extern declaration and use it from another C file.
Problem 19
Create a static helper function that cannot be referenced from another source file.
Problem 20
Create a small multi-file C project demonstrating auto, register, static and extern.
22.36 Interview Questions
Q2. What is the difference between scope and lifetime?
Q3. Why is static local useful?
Q4. What is internal linkage?
Q5. What is external linkage?
Q6. Does register guarantee register storage?
Q7. What is the purpose of extern?
Q8. What is the difference between a declaration and a definition?
Q9. What happens to a static local variable after a function returns?
Q10. Why might static be used for a helper function?
22.37 Key Takeaway
auto → ordinary local variable.
register → optimization request.
static local → remembers its value.
static file-scope → private to the translation unit.
extern → refers to an entity with linkage defined elsewhere.
Scope tells you WHERE a name can be used.
Lifetime tells you HOW LONG the object exists.
Linkage tells you whether declarations can refer to the same entity.
🛠️ 23. Advanced C Preprocessor
Since the basic Preprocessor section is already covered, this section focuses on advanced concepts such as macros, conditional compilation, header guards, stringizing, token pasting, and predefined macros.
23.1 What is the C Preprocessor?
The C preprocessor processes source code before the actual compilation takes place.
Important preprocessing directives include:
#include
#define
#ifdef
#ifndef
#if
#elif
#else
#endif
#undef
23.2 Header Files
Header files usually contain declarations, macros, type definitions, and function prototypes that can be shared between source files.
#include <stdio.h>
A user-defined header can be included using:
#include "myheader.h"
23.3 System Header vs User Header
| Syntax | Typical Use |
|---|---|
#include <file.h> |
System / library header |
#include "file.h" |
User-defined / project header |
23.4 Object-Like Macros
A macro without parameters is called an object-like macro.
#define PI 3.14159
Example:
#include <stdio.h>
#define PI 3.14159
int main()
{
printf("%f", PI);
return 0;
}
23.5 Function-Like Macros
A macro can accept arguments. Such a macro is called a function-like macro.
#define SQUARE(x) ((x) * (x))
#include <stdio.h>
#define SQUARE(x) ((x) * (x))
int main()
{
printf("%d", SQUARE(5));
return 0;
}
23.6 Why Parentheses Matter in Macros
Unsafe macro:
#define SQUARE(x) x * x
Consider:
SQUARE(2 + 3)
Expansion:
2 + 3 * 2 + 3
Correct version:
#define SQUARE(x) ((x) * (x))
Now:
SQUARE(2 + 3)
becomes:
((2 + 3) * (2 + 3))
23.7 Macro Arguments and Side Effects
Consider:
#define SQUARE(x) ((x) * (x))
int i = 5;
int result = SQUARE(i++);
i++ appears more than once after
macro expansion. Avoid using expressions with side effects
as arguments to macros that may evaluate them multiple times.
A normal function is usually safer in this situation.
23.8 Macro vs Function
| Feature | Macro | Function |
|---|---|---|
| Processed by | Preprocessor | Compiler |
| Type checking | No direct type checking | Yes |
| Arguments | Text substitution | Function parameters |
| Debugging | Can be harder | Usually easier |
| Side effects | Can cause repeated evaluation | Parameters are evaluated for the call |
23.9 # Stringizing Operator
The # operator converts a macro argument into a
string literal.
#define STRINGIFY(x) #x
printf("%s", STRINGIFY(Hello));
Practical Example
#define SHOW(x) printf("%s = %d\n", #x, x)
int age = 20;
SHOW(age);
23.10 ## Token-Pasting Operator
The ## operator combines two preprocessing tokens
into one token.
#define CONCAT(a,b) a##b
Example:
int CONCAT(num,1) = 100;
After preprocessing, this produces:
int num1 = 100;
23.11 Multi-Line Macro
A backslash \ can be used to continue a macro
definition onto the next line.
#define PRINT_NUMBERS() \
do \
{ \
printf("10\n"); \
printf("20\n"); \
printf("30\n"); \
} while (0)
The do { } while (0) technique makes a
multi-statement macro behave more like a single statement.
23.12 Why Use do { } while (0)?
Consider a multi-statement macro:
#define SET_VALUES() \
x = 10; \
y = 20;
Such a macro can cause problems when used inside an
if-else statement.
Safer version:
#define SET_VALUES() \
do \
{ \
x = 10; \
y = 20; \
} while (0)
23.13 Conditional Compilation
Conditional compilation allows selected sections of source code to be included or excluded before compilation.
#ifdef DEBUG
printf("Debug mode");
#endif
23.14 #ifdef
#ifdef checks whether a macro has been defined.
#define DEBUG
#ifdef DEBUG
printf("Debug mode enabled");
#endif
23.15 #ifndef
#ifndef means "if not defined".
#ifndef MAX_SIZE
#define MAX_SIZE 100
#endif
23.16 Include Guards
Include guards prevent the contents of a header file from being processed repeatedly.
#ifndef STUDENT_H
#define STUDENT_H
void displayStudent();
#endif
23.17 Include Guard Flow
Include student.h
|
v
Is STUDENT_H defined?
/ \
Yes No
| |
v v
Skip Define STUDENT_H
|
v
Process header
|
v
End
23.18 #if
#if allows conditional compilation based on a
preprocessing expression.
#define VERSION 2
#if VERSION == 2
printf("Version 2");
#endif
23.19 #elif
#define VERSION 2
#if VERSION == 1
printf("Version 1");
#elif VERSION == 2
printf("Version 2");
#else
printf("Unknown version");
#endif
23.20 #else
#define DEBUG 0
#if DEBUG
printf("Debug mode");
#else
printf("Normal mode");
#endif
23.21 #undef
The #undef directive removes a macro definition.
#define VALUE 100
#undef VALUE
After #undef, the macro VALUE is no
longer defined.
23.22 Predefined Macros
Common predefined macros include:
| Macro | Meaning |
|---|---|
__FILE__ |
Current source file name |
__LINE__ |
Current source line number |
__DATE__ |
Compilation date |
__TIME__ |
Compilation time |
23.23 __FILE__
#include <stdio.h>
int main()
{
printf("File: %s", __FILE__);
return 0;
}
23.24 __LINE__
#include <stdio.h>
int main()
{
printf("Current line: %d", __LINE__);
return 0;
}
23.25 __DATE__
#include <stdio.h>
int main()
{
printf("Compiled on: %s", __DATE__);
return 0;
}
23.26 __TIME__
#include <stdio.h>
int main()
{
printf("Compiled at: %s", __TIME__);
return 0;
}
23.27 Debugging with Predefined Macros
#define DEBUG_PRINT(x) \
printf("[%s:%d] %s = %d\n", __FILE__, __LINE__, #x, x)
Example:
int value = 50;
DEBUG_PRINT(value);
This can display the source file, line number, variable name, and variable value.
23.28 Debug and Release Versions
#ifdef DEBUG
#define LOG(x) printf("DEBUG: %s\n", x)
#else
#define LOG(x)
#endif
This allows debugging messages to be enabled or disabled through conditional compilation.
23.29 Platform-Specific Compilation
#ifdef _WIN32
printf("Windows");
#elif defined(__linux__)
printf("Linux");
#else
printf("Other platform");
#endif
The exact predefined platform macros depend on the compiler and toolchain.
23.30 Feature Selection
#define ENABLE_AUDIO
#ifdef ENABLE_AUDIO
void playAudio()
{
printf("Audio enabled");
}
#endif
23.31 Macro Constants
#define MAX_STUDENTS 100
#define PASS_MARK 40
#define COLLEGE_NAME "ABC College"
printf("%d", MAX_STUDENTS);
23.32 Macro Function for Maximum
#define MAX(a,b) ((a) > (b) ? (a) : (b))
int x = 10;
int y = 20;
printf("%d", MAX(x, y));
23.33 Macro Function for Minimum
#define MIN(a,b) ((a) < (b) ? (a) : (b))
printf("%d", MIN(10, 20));
23.34 Macro for Swapping
#define SWAP(a,b,temp) \
do \
{ \
temp = a; \
a = b; \
b = temp; \
} while (0)
int a = 10;
int b = 20;
int temp;
SWAP(a, b, temp);
b = 10
23.35 Macro Pitfall: Operator Precedence
Bad macro:
#define DOUBLE(x) x + x
Consider:
3 * DOUBLE(4)
Expansion:
3 * 4 + 4
Correct macro:
#define DOUBLE(x) ((x) + (x))
23.36 Macro Pitfall: Side Effects
#define SQUARE(x) ((x) * (x))
Avoid:
SQUARE(i++);
23.37 Macro Pitfall: Missing Parentheses
Bad:
#define ADD(a,b) a + b
10 * ADD(2,3)
Expansion:
10 * 2 + 3
Correct:
#define ADD(a,b) ((a) + (b))
23.38 Macro vs const
Macro:
#define MAX_SIZE 100
Constant object:
const int max_size = 100;
A macro is handled during preprocessing, whereas
const is part of the C language type system.
23.39 Macro vs enum
enum
{
RED = 1,
GREEN = 2,
BLUE = 3
};
Enumerations are typed language constructs with their own semantics, while macros are preprocessing substitutions.
23.40 Practical Include Guard Example
student.h
#ifndef STUDENT_H
#define STUDENT_H
typedef struct
{
int id;
char name[50];
} Student;
void displayStudent(Student s);
#endif
student.c
#include <stdio.h>
#include "student.h"
void displayStudent(Student s)
{
printf("ID: %d\n", s.id);
printf("Name: %s\n", s.name);
}
main.c
#include "student.h"
int main()
{
Student s = {101, "Venu"};
displayStudent(s);
return 0;
}
Name: Venu
23.41 Preprocessing Flow
C Source Code
|
v
Preprocessor
|
+-------+-------+
| | |
#include #define Conditional
compilation
|
v
Macro Expansion
|
v
Preprocessed Source
|
v
Compiler
|
v
Object Code
|
v
Linker
|
v
Executable Program
23.42 Common Mistakes
- Forgetting parentheses in function-like macros.
- Using macros with arguments that have side effects.
- Confusing macro expansion with function calls.
- Forgetting include guards in reusable headers.
- Misusing
#ifdefinstead of#if. - Forgetting that
#undefremoves a macro. - Using
__DATE__and__TIME__as runtime values. - Writing multi-statement macros without
do { } while (0). - Using macros where a normal function would be clearer.
23.43 Quick Revision
#define → Defines a macro.
📌
#undef → Removes a macro definition.
📌
#include → Includes a header.
📌
#ifdef → Checks whether a macro is defined.
📌
#ifndef → Checks whether a macro is not defined.
📌
#if → Conditional preprocessing expression.
📌
#elif → Additional condition.
📌
#else → Alternative branch.
📌
#endif → Ends conditional compilation.
📌
# → Stringizes a macro argument.
📌
## → Joins preprocessing tokens.
📌
__FILE__ → Current source file.
📌
__LINE__ → Current source line.
📌
__DATE__ → Compilation date.
📌
__TIME__ → Compilation time.
23.44 Quick MCQs
1. Which directive defines a macro?
A) #include
B) #define
C) #ifdef
D) #undef
Answer: B
2. Which directive removes a macro definition?
A) #delete
B) #remove
C) #undef
D) #clear
Answer: C
3. Which operator stringizes a macro argument?
A) ##
B) #
C) @
D) $
Answer: B
4. Which operator performs token pasting?
A) #
B) ##
C) &&
D) ::
Answer: B
5. Which directive is commonly used for include guards?
A) #ifndef
B) #include
C) #pragma
D) #undef
Answer: A
6. What does __LINE__ represent?
A) File name
B) Compilation date
C) Current source line number
D) Program size
Answer: C
7. What does __FILE__ provide?
A) Current source file name
B) Current function name
C) Current line number
D) Current date
Answer: A
8. Which technique is commonly used for multi-statement macros?
A) if
B) do { } while (0)
C) switch
D) goto
Answer: B
9. Which stage processes #define?
A) Linker
B) Loader
C) Preprocessor
D) CPU
Answer: C
10. What is a common problem with function-like macros?
A) They cannot accept arguments
B) Arguments can be evaluated more than once
C) They always require dynamic memory
D) They cannot work with integers
Answer: B
23.45 Practice Problems
Problem 1:
Define a macro named PI and use it to calculate
the area of a circle.
Problem 2:
Create a function-like macro SQUARE(x).
Problem 3:
Explain why #define SQUARE(x) x * x is unsafe.
Problem 4:
Create a macro named MAX(a,b).
Problem 5:
Create a macro named MIN(a,b).
Problem 6:
Write a macro that prints the name and value of a variable
using the # operator.
Problem 7:
Write a macro that combines two tokens using ##.
Problem 8:
Create a multi-statement macro using do { } while (0).
Problem 9:
Create an include guard for college.h.
Problem 10:
Write a program using #ifdef DEBUG.
Problem 11:
Write a program using #if, #elif
and #else.
Problem 12:
Write a program that prints __FILE__.
Problem 13:
Write a program that prints __LINE__.
Problem 14:
Write a program that prints __DATE__ and
__TIME__.
Problem 15:
Create a macro DOUBLE(x) that correctly handles
DOUBLE(2 + 3).
Problem 16:
Explain why SQUARE(i++) can be dangerous.
Problem 17:
Create a project containing main.c,
student.c and student.h.
Problem 18:
Create a debugging macro using __FILE__,
__LINE__ and #.
Problem 19: Explain the difference between a macro and a function.
Problem 20:
Create a small program demonstrating
#define, #undef, conditional
compilation, #, ##,
__FILE__ and __LINE__.
23.46 Interview Questions
Q1. What is the C preprocessor?
Q2. What is the difference between a macro and a function?
Q3. What is an object-like macro?
Q4. What is a function-like macro?
Q5. Why should macro arguments normally be parenthesized?
Q6. What is the purpose of the # operator?
Q7. What is the purpose of ##?
Q8. What are include guards?
Q9. Why is do { } while (0) used in multi-statement macros?
Q10. What is conditional compilation?
Q11. What is the difference between #ifdef and #if?
Q12. What does #undef do?
Q13. What are predefined macros?
Q14. What is the difference between #define and const?
Q15. Why can macros be dangerous when arguments have side effects?
23.47 Key Takeaway
🎯 Use parentheses carefully in function-like macros.
🎯 Avoid expressions with side effects as macro arguments when they may be evaluated multiple times.
🎯 Use include guards in reusable header files.
🎯
# converts a macro argument into a string.
🎯
## combines preprocessing tokens.
🎯 Conditional compilation is useful for debugging, platform-specific code and optional features.
🎯 Prefer normal functions when they provide clearer and safer behavior than macros.
✍️ C Programming Practice
Practice C programming problems from basic to placement level.
Choose any level based on your requirement. You can practice the levels in any order.
💡 Recommendation: Start from Level 1 and gradually move towards placement-level problems.
🎯 Choose Your Practice Level
Click any level to open or close its practice problems.