💻 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.

Important Timeline

🔹 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.

💡 Learning C helps you understand:

✔ 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

Hello, World!

1.8 Line-by-Line Explanation

#include <stdio.h>
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?

Source Code ↓ Preprocessor ↓ Compiler ↓ Object Code ↓ Linker ↓ Executable File ↓ Program Output

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.

💡 Example:

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 was developed by Dennis Ritchie.

📌 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

  1. Who developed the C programming language?
  2. What type of programming language is C?
  3. What is the purpose of main()?
  4. What is the purpose of printf()?
  5. What is a compiler?
  6. What is the purpose of #include?
  7. What is a pointer?
  8. Why is C called a structured language?
  9. Give three applications of C.
  10. Why is C important for learning Data Structures?

1.14 Beginner Programming Problems

Problem 1: Write a C program to print your name.

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

🎯 Remember:

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

1. Preprocessor Directives
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().

💡 Remember:

#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;
}
int
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);
⚠️ Forgetting a semicolon is one of the most common errors made by beginners.

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);
💡 The & before the variable is important for many scanf() calls because scanf() needs the variable's address to store the entered value.

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:

30

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

Enter two numbers: 10 20

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.

INPUT
Values entered by the user



PROCESSING
Calculations or logic



OUTPUT
Final result

Example

Problem: Add two numbers.

Input: a = 10, b = 20

Processing: sum = a + b

Output: 30

2.17 Program Execution Flow

C Source Code

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

📌 #include is a preprocessor directive.

📌 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

  1. What is the purpose of #include?
  2. What is the role of stdio.h?
  3. What is the purpose of main()?
  4. Why are curly braces used?
  5. Why is a semicolon used in C?
  6. What are comments?
  7. What is the purpose of scanf()?
  8. What is the purpose of printf()?
  9. What does return 0 mean when returned from main()?
  10. Explain the Input → Processing → Output model with an example.

2.21 Programming Problems

Problem 1
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

🎯 When solving a C programming problem, first identify:

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.

Example:

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);
21

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.

💡 Remember:

= 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.

💡 age, Age and AGE are different names in C.

3.10 What is a Constant?

A constant is a value that is intended not to change during the relevant part of a program.

Examples:

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 */
💡 Use const when a value should not be modified through that variable.

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;
Variable

┌─────────────────────┐
│ 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;
💡 For readability, separate declarations are often easier to understand in larger programs.

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

Age = 20
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

📌 A variable represents a storage location used by a program.

📌 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

  1. Which symbol is used for assignment?

    A) ==
    B) =
    C) !=
    D) >

    Answer: B

  2. Which is a valid identifier?

    A) 2value
    B) student name
    C) student_name
    D) float

    Answer: C

  3. Which keyword can be used to qualify an object as read-only?

    A) fixed
    B) constant
    C) const
    D) readonly

    Answer: C

  4. Which of the following is case-sensitive in C?

    A) Identifiers
    B) Spaces
    C) Comments
    D) Output

    Answer: A

  5. Which is a valid declaration?

    A) int 2age;
    B) int age;
    C) integer age;
    D) number age;

    Answer: B

3.19 Programming Problems

Problem 1
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

🎯 Before writing a C program, ask:

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.

Examples:

int → Whole numbers
float → Decimal numbers
double → Double-precision floating-point values
char → Character values

4.2 Main Categories of C Data Types

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);
A

Character constants are written using single quotes.

'A'

'B'

'7'

'@'
💡 A character such as '5' is not the same as the integer 5.

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.

Examples:

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);
36.50

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);
3.141592653589793

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.

Common keywords:

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;
💡 An unsigned integer type cannot represent negative values.

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
💡 For many general-purpose calculations, double is preferred when extra precision is useful. The actual representation and precision are implementation-dependent.

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);
2.500000

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);
2

If a fractional result is required, convert at least one operand to a floating-point type.

printf("%f", (double)a / b);
2.500000

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

📌 char is used for character values.

📌 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

  1. Which data type is commonly used to store an integer?

    A) float
    B) int
    C) char
    D) void

    Answer: B

  2. Which operator gives the size of a type or object?

    A) size
    B) length
    C) sizeof
    D) bytes

    Answer: C

  3. Which is a character constant?

    A) "A"
    B) 'A'
    C) A
    D) `A`

    Answer: B

  4. What is the result of 5 / 2 when both operands are int?

    A) 2.5
    B) 3
    C) 2
    D) 0

    Answer: C

  5. Which type generally provides more precision than float?

    A) char
    B) int
    C) double
    D) void

    Answer: C

4.22 Programming Problems

Problem 1
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

🎯 Don't simply memorize data types.

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.

INPUT


PROCESSING


OUTPUT
Example:

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;
}
Hello World

5.3 Printing Text

Text written inside double quotation marks is printed as a string.

printf("Welcome to C Programming");
Welcome to C Programming

5.4 Printing Variables

Format specifiers are used to display values stored in variables.

int age = 25;

printf("%d", age);
25

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);
100

5.7 Printing Characters

char grade = 'A';

printf("%c", grade);
A
💡 Character constants use single quotes:

'A'

Strings use double quotes:

"A"

5.8 Printing Floating-Point Values

float marks = 85.5f;

printf("%f", marks);
85.500000

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);
85.57

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");
Hello
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);
age

Memory location

&age

Address of age
💡 For a normal integer variable:

scanf("%d", &age);

The & is important.

5.13 Reading Multiple Values

int a, b;

scanf("%d %d", &a, &b);

If the input is:

10 20

then:

a = 10
b = 20

5.14 Reading Different Data Types

int age;
float marks;
char grade;

scanf("%d", &age);

scanf("%f", &marks);

scanf(" %c", &grade);
Notice the space before %c:

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);
📌 Remember:

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.

⚠️ %s with scanf() stops reading at whitespace.

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

20
85.5

Sample Output

Age = 20
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

10 20

Output

Sum = 30

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

📌 printf() is commonly used for formatted output.

📌 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

  1. Which function is commonly used to display formatted output?

    A) scanf()
    B) printf()
    C) input()
    D) read()

    Answer: B

  2. Which function is commonly used to read formatted input?

    A) printf()
    B) display()
    C) scanf()
    D) output()

    Answer: C

  3. Which format specifier is commonly used for an int?

    A) %f
    B) %c
    C) %d
    D) %s

    Answer: C

  4. Which specifier is used to read a double using scanf()?

    A) %d
    B) %f
    C) %lf
    D) %c

    Answer: C

  5. Which escape sequence moves the cursor to the next line?

    A) \t
    B) \n
    C) \\
    D) \a

    Answer: B

5.24 Programming Problems

Problem 1
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

🎯 For every input/output problem, remember:

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.

Example:

a + b

+ → Operator
a, b → Operands

6.2 Types of Operators in C

Operators

├── 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);
13
7
30
3
1

6.5 Modulus Operator %

The modulus operator gives the remainder of an integer division.

10 % 3
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);
2

If you need a fractional result, use a floating-point operand.

printf("%f", (double)a / b);
2.500000

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);
0
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);
15

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 && TRUE → TRUE
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 || TRUE → 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

6.15 Decrement Operator --

The decrement operator decreases a modifiable integer object by one.

int a = 5;

a--;

printf("%d", a);
4

6.16 Pre-Increment vs Post-Increment

Pre-Increment

int a = 5;

int b = ++a;
First increase a
a = 6
Then assign 6 to b

a = 6
b = 6

Post-Increment

int a = 5;

int b = a++;
First use old value
b = 5
Then increase a
a = 6
💡 Easy memory trick:

++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);
20
condition ? value_if_true : value_if_false

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:

5 → 0101
3 → 0011
printf("%d", a & b);
1

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);
6
5 → 0101
3 → 0011
XOR
0110 → 6

6.21 Shift Operators

Left Shift

int x = 5;

printf("%d", x << 1);
10

Right Shift

int x = 8;

printf("%d", x >> 1);
4

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 + (5 × 2)
= 10 + 10
= 20

Use Parentheses for Clarity

int result = (10 + 5) * 2;
30
💡 When an expression is difficult to read, use parentheses to make the intended grouping explicit.

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
(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

📌 + → Addition

📌 - → Subtraction

📌 * → Multiplication

📌 / → Division

📌 % → Remainder

📌 == → Equality comparison

📌 = → Assignment

📌 && → Logical AND

📌 || → Logical OR

📌 ! → Logical NOT

📌 ++ → Increment

📌 -- → Decrement

📌 ?: → Conditional operator

📌 & | ^ ~ << >> → Bitwise operators

6.27 Quick MCQs

  1. What is the result of 10 % 3?

    A) 0
    B) 1
    C) 3
    D) 10

    Answer: B

  2. Which operator is used for equality comparison?

    A) =
    B) ==
    C) !=
    D) >=

    Answer: B

  3. What is the result of 5 / 2 when both operands are integers?

    A) 2.5
    B) 3
    C) 2
    D) 1

    Answer: C

  4. Which operator means logical AND?

    A) &
    B) &&
    C) |
    D) ||

    Answer: B

  5. What is the value of a after:

    int a = 5;
    ++a;

    A) 4
    B) 5
    C) 6
    D) 7

    Answer: C

  6. Which operator is used for bitwise XOR?

    A) &
    B) |
    C) ^
    D) ~

    Answer: C

6.28 Programming Problems

Problem 1
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

🎯 Operators are the building blocks of expressions and conditions.

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.

Example:

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.

result = 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 = 13
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);
1

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);
1

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.

💡 Assignment itself is an expression in C, and its value is the value assigned.

7.8 Mixed Expressions

An expression may contain several different operators.

int result = 10 + 5 * 2;

Multiplication has higher precedence than addition.

10 + (5 × 2)
= 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;
Step 1:
5 × 2 = 10

Step 2:
10 + 10 = 20
result = 20

7.11 Parentheses Change the Result

int result = (10 + 5) * 2;
Step 1:
(10 + 5) = 15

Step 2:
15 × 2 = 30
result = 30
💡 Parentheses have very high precedence and are the best way to make the intended grouping obvious.

7.12 Associativity

Associativity determines how operators with the same precedence are grouped.

Left-to-Right Example

int result = 20 - 5 - 3;
(20 - 5) - 3
= 15 - 3
= 12

Multiplication and Division

int result = 20 / 5 * 2;
(20 / 5) × 2
= 4 × 2
= 8

7.13 Integer Expression

int a = 5;
int b = 2;

int result = a / b;
result = 2

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);
2.50
📌 Important:

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;
2.500000

7.16 Type Casting

A cast has the form:

(data_type) expression

Example

int marks = 85;
int total = 100;

float percentage =
    (float)marks / total * 100;

printf("%.2f", percentage);
85.00

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");
}
True
int a = 0;

if (a)
{
    printf("True");
}
else
{
    printf("False");
}
False

7.19 Logical Expression Evaluation

int a = 10;
int b = 20;

int result = (a < b) && (b > 15);
a < b → TRUE
b > 15 → TRUE

TRUE && TRUE
= TRUE

7.20 Conditional Expression

int a = 10;
int b = 20;

int max = (a > b) ? 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.

20 / 5 = 4

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;
b × 2
= 20

a + 20
= 25

7.23 Increment Expressions

Pre-Increment

int a = 5;

int b = ++a;
First:
a = 6

Then:
b = 6

Final:
a = 6, b = 6

Post-Increment

int a = 5;

int b = a++;
First:
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
⚠️ Such expressions can lead to undefined behavior in C.

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

📌 Expression → Combination of operands and operators that produces a value.

📌 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

  1. What is the result of:

    10 + 5 * 2

    A) 30
    B) 20
    C) 25
    D) 15

    Answer: B

  2. What is the result of:

    5 / 2

    when both operands are int?

    A) 2.5
    B) 3
    C) 2
    D) 1

    Answer: C

  3. Which operator has higher precedence?

    A) +
    B) *
    C) =
    D) ||

    Answer: B

  4. What is the result of:

    (10 + 5) * 2

    A) 20
    B) 25
    C) 30
    D) 15

    Answer: C

  5. What is the value of b?

    int a = 5;
    int b = a++;

    A) 4
    B) 5
    C) 6
    D) Undefined

    Answer: B

  6. What is the result of:

    (double)5 / 2

    A) 2
    B) 2.0
    C) 2.5
    D) 3

    Answer: C

7.28 Practice Problems

Problem 1
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

🎯 Before evaluating any C expression:

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.

Input

Condition

Decision

Output
Real-life example:

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.

if (condition)
{
    statements;
}

Example

int age = 20;

if (age >= 18)
{
    printf("Eligible");
}
Eligible

How it works

age = 20

age >= 18 ?

TRUE

Print "Eligible"

8.4 if-else Statement

The if-else statement provides two possible paths.

if (condition)
{
    statement 1;
}
else
{
    statement 2;
}

Example: Pass or Fail

int marks = 35;

if (marks >= 40)
{
    printf("Pass");
}
else
{
    printf("Fail");
}
Fail

8.5 Example: Even or Odd

int n;

scanf("%d", &n);

if (n % 2 == 0)
{
    printf("Even");
}
else
{
    printf("Odd");
}
Logic:

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.

if (condition1)

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");
}
💡 Put more restrictive or higher-range conditions first when using a descending grade structure.

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");
    }
}
age >= 18 ?

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);
}
Example Input:
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);
}
Input:
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");
}
Example:

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.

switch(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;
}
One
⚠️ Without break, execution can continue into subsequent cases. This is called fall-through.

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");
}
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 1 → Understand the problem

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.

Input → n

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 → One condition

📌 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

  1. Which statement is used to make a decision based on a condition?

    A) for
    B) if
    C) printf
    D) scanf

    Answer: B

  2. Which statement provides two alternatives?

    A) if
    B) if-else
    C) switch only
    D) for

    Answer: B

  3. Which keyword terminates a switch case when fall-through is not intended?

    A) stop
    B) exit
    C) break
    D) continue

    Answer: C

  4. Which keyword handles unmatched switch cases?

    A) else
    B) default
    C) otherwise
    D) none

    Answer: B

  5. What is the output?

    int n = 10;
    if (n > 5)
    {
      printf("Yes");
    }

    A) No
    B) Yes
    C) 10
    D) Error

    Answer: B

  6. 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

Problem 1
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

🎯 Decision making is the bridge between understanding a problem and writing a program.

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.

Real-life example:

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.

for(initialization; condition; update)
{
    statements;
}

Example

for(int i = 1; i <= 5; i++)
{
    printf("%d\n", i);
}
1
2
3
4
5

9.5 How for Loop Works

Initialization

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.

while(condition)
{
    statements;
}

Example

int i = 1;

while(i <= 5)
{
    printf("%d\n", i);
    i++;
}
1
2
3
4
5

9.7 How while Loop Works

Initialize

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.

do
{
    statements;
}
while(condition);

Example

int i = 1;

do
{
    printf("%d\n", i);
    i++;
}
while(i <= 5);
1
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");
}
No output
int i = 10;

do
{
    printf("Hello");
}
while(i < 5);
Hello
💡 Remember:

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);
}
1 2 3 4 5 6 7 8 9 10

9.11 Counting in Reverse

for(int i = 10; i >= 1; i--)
{
    printf("%d ", i);
}
10 9 8 7 6 5 4 3 2 1

9.12 Print Even Numbers

for(int i = 2; i <= 20; i += 2)
{
    printf("%d ", i);
}
2 4 6 8 10 12 14 16 18 20

9.13 Print Odd Numbers

for(int i = 1; i <= 20; i += 2)
{
    printf("%d ", i);
}
1 3 5 7 9 11 13 15 17 19

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);
Input:
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.

5! = 5 × 4 × 3 × 2 × 1
= 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);
}
Input:
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);
Input: 5832

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);
Input:
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.

121 → Palindrome
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.

2 → Prime
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);
}
1 2 3 4

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);
}
1 2 4 5

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);
}
1 5
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");
}
⚠️ Be careful with infinite loops.

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 1 → Identify what must repeat.

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

Start → i = 1
Stop → i <= N
Change → i++
Operation → sum = sum + i

9.31 Quick Revision

📌 for → Commonly used when iteration count is known.

📌 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

  1. Which loop is commonly used when the number of iterations is known?

    A) if
    B) for
    C) switch
    D) goto

    Answer: B

  2. Which loop executes its body at least once?

    A) for
    B) while
    C) do-while
    D) if

    Answer: C

  3. Which keyword terminates a loop?

    A) continue
    B) break
    C) stop
    D) exitloop

    Answer: B

  4. Which keyword skips the current iteration?

    A) break
    B) skip
    C) continue
    D) next

    Answer: C

  5. 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

  6. 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

Problem 1
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

🎯 Every loop has three important ideas:

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.

Suppose we want to store 5 marks:

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;
💡 Arrays are especially useful when we need to process many values using loops.

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
First element → arr[0]

Second element → arr[1]

Last element → arr[n-1]

10.4 Array Declaration

data_type array_name[size];

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[0] = 10
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 20 0 0 0

10.8 Accessing Array Elements

int numbers[5] = {10, 20, 30, 40, 50};

printf("%d", numbers[2]);
30

10.9 Modifying an Array Element

int numbers[5] = {10, 20, 30, 40, 50};

numbers[2] = 100;

printf("%d", numbers[2]);
100

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]);
}
Array + Loop

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);
Input:
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);
Input:
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");
}
Array:
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]);
}
Input:
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);
⚠️ For a fully robust implementation, the program should also handle cases where there are fewer than two distinct values.

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.

data_type array_name[rows][columns];
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 1 → Read N

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.

Input Array

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

📌 Array → Collection of same-type elements.

📌 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

  1. What is the index of the first element of a C array?

    A) 0
    B) 1
    C) -1
    D) 2

    Answer: A

  2. What is the last index of an array containing n elements?

    A) n
    B) n + 1
    C) n - 1
    D) 0

    Answer: C

  3. Which loop is commonly used to traverse an array?

    A) for
    B) switch
    C) if
    D) goto

    Answer: A

  4. 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

  5. Which array represents rows and columns?

    A) 1D array
    B) 2D array
    C) Pointer
    D) Structure

    Answer: B

10.44 Practice Problems

Problem 1
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

🎯 Remember the core array pattern:

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.

Example:

"HELLO"

Internally, C stores it as:

H   E   L   L   O   \0
💡 Important:

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.

V   e   n   u   \0

11.5 String Size

The array must have enough space for all characters plus the null character.

char name[5] = "Venu";
V + e + n + u + \0

Total = 5 characters
⚠️ If a string contains N visible characters, it normally requires at least N + 1 array positions to store the terminating \0.

11.6 Printing a String

char name[] = "Venu";

printf("%s", name);
Venu

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]);
V
e
n
u

11.8 Traversing a String

char str[] = "HELLO";

for(int i = 0; str[i] != '\0'; i++)
{
    printf("%c ", str[i]);
}
H E L L O

11.9 Reading a Single Word using scanf()

char name[20];

scanf("%s", name);

printf("%s", name);
Input:
Venu

Output:
Venu
⚠️ scanf("%s", name) normally reads only until whitespace.

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);
Input:
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;
}
5

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);
Hello

11.14 strcat()

The strcat() function appends one string to another.

char first[30] = "Hello ";
char second[] = "World";

strcat(first, second);

printf("%s", first);
Hello World

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");
}
Equal
💡 For equality, use:

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]);
}
Input:
HELLO

Output:
OLLEH

11.24 Palindrome String

A string is a palindrome if it reads the same from both directions.

MADAM → Palindrome

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]);
}
Venu
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.

"CAT"

C → A → T → \0

The \0 tells C that the string has ended.

⚠️ \0 is not the same as the character '0'.

'\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

📌 C has no built-in string data type.

📌 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

  1. Which character terminates a C string?

    A) '\n'
    B) '\0'
    C) '0'
    D) '\t'

    Answer: B

  2. Which format specifier is used to print a string?

    A) %c
    B) %d
    C) %s
    D) %f

    Answer: C

  3. Which function finds the length of a string?

    A) strcpy()
    B) strlen()
    C) strcat()
    D) strcmp()

    Answer: B

  4. Which function compares two strings?

    A) strcpy()
    B) strlen()
    C) strcmp()
    D) strcat()

    Answer: C

  5. Which function concatenates strings?

    A) strcat()
    B) strlen()
    C) strcmp()
    D) strchr()

    Answer: A

  6. Which function can read a line containing spaces?

    A) scanf("%s")
    B) fgets()
    C) strlen()
    D) strcpy()

    Answer: B

11.39 Practice Problems

Problem 1
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

🎯 Remember the basic string pattern:

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.

Instead of writing one very large program, we can divide it into smaller functions.

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.
💡 Think of a function as a small machine:

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;
}
Hello!

12.5 Calling a Function

A function executes when it is called.

greet();
Function Definition

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;
}
30

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;
}
25

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.

Function

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;
}
⚠️ Every recursive function should have a suitable base case to stop recursion.

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;
}
💡 This simple recursive Fibonacci implementation is useful for understanding recursion, but it is inefficient for large n.

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;
}
1
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;
}
10
📌 The original variable is not changed by the function in this example.

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;
}
100

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

main()

call function

function receives arguments

function performs task

return result

main() continues

12.39 How to Design a Good Function

  1. Give the function one clear responsibility.
  2. Use a meaningful function name.
  3. Keep the function reasonably small.
  4. Pass only the data the function needs.
  5. Return a useful result when appropriate.
  6. Avoid unnecessary global variables.

12.40 Function-Based Problem Solving

Problem

Break into smaller tasks

Create functions

Call functions

Combine results
Example: Find the average of an array.

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

📌 Function → Reusable block of code.

📌 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

  1. What is a function?

    A) A variable
    B) A reusable block of code
    C) A data type
    D) An operator

    Answer: B

  2. Which keyword indicates that a function returns no value?

    A) null
    B) empty
    C) void
    D) zero

    Answer: C

  3. What is recursion?

    A) A loop
    B) A function calling itself
    C) A variable assignment
    D) An array

    Answer: B

  4. What should recursive functions normally have to stop recursion?

    A) Pointer
    B) Array
    C) Base case
    D) Structure

    Answer: C

  5. Which keyword is used to send a value back from a function?

    A) break
    B) return
    C) continue
    D) goto

    Answer: B

  6. 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

Problem 1
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

🎯 The most important idea:

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 → stores 10

&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.

Variable

Memory Address

Stored Value
int x = 10;

printf("Value = %d\n", x);
printf("Address = %p\n", (void*)&x);
💡 The actual memory address can be different every time the program runs.

13.4 Address Operator &

The & operator gives the memory address of a variable.

int x = 25;

printf("%p", (void*)&x);
x = 25

&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;
p

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);
10
p → address

*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;
}
🎯 Remember:

&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);
100

Changing *p changes the original variable because the pointer points to that variable.

```html

13.10 Pointer Memory Diagram

x
10
Address: 1000
──────────────►
p points to x
p
1000
Address: 2000
Important:

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);
}
⚠️ Never dereference a null pointer.

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);
10

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));
}
10 20 30 40

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);
10
20

13.16 Pointer Expressions

*(p + 0)
*(p + 1)
*(p + 2)
*(p + i) → element at index i

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);
3

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;
}
100

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;
}
20 10

13.20 Pointer and String

char str[] = "HELLO";

char *p = str;

while(*p != '\0')
{
    printf("%c ", *p);

    p++;
}
H E L L O

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
10
q → p → x

**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;
}
10 20 30 40 50

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);
0 0 0 0 0

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);
💡 Using a temporary pointer with realloc() helps avoid losing the original allocation if realloc() fails.

13.28 free()

free() releases dynamically allocated memory.

int *p = malloc(10 * sizeof(int));

if(p != NULL)
{
    free(p);

    p = NULL;
}
⚠️ Memory allocated dynamically should be released when it is no longer needed.

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]);
}
10 20 30

13.33 Pointer to an Array

int arr[3] = {10, 20, 30};

int (*p)[3] = &arr;

printf("%d", (*p)[1]);
20

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;
}
20

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;
}
30

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");
}
10 20 30
40 50 60

13.38 Pointer Formula

int x = 10;

&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

📌 Pointer → stores an address.

📌 &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

  1. What does a pointer store?

    A) Only integers
    B) A memory address
    C) A function name only
    D) A keyword

    Answer: B

  2. Which operator obtains the address of a variable?

    A) *
    B) &
    C) %
    D) #

    Answer: B

  3. Which operator dereferences a pointer?

    A) *
    B) &
    C) #
    D) %

    Answer: A

  4. 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

  5. Which function allocates dynamic memory?

    A) printf()
    B) malloc()
    C) scanf()
    D) strlen()

    Answer: B

  6. Which function releases dynamically allocated memory?

    A) delete()
    B) remove()
    C) free()
    D) clear()

    Answer: C

13.42 Practice Problems

Problem 1
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

🎯 Remember this three-part rule:

&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.

Student

├── 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;
}
Roll = 101
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;
}
101
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
};
💡 Designated initialization makes the code easier to understand, especially when a structure has many members.

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;
}
10 20

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 variable → use .

📌 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);
2000

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;
};
Structure

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.

union Data
Shared Memory
int
float
char
All members use the same memory location

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;
};
🎯 This concept is the foundation of Linked Lists and many other data structures.

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 → Groups different data types.

📌 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

  1. Which keyword is used to define a structure?

    A) record
    B) struct
    C) structure
    D) object

    Answer: B

  2. Which operator accesses a structure member?

    A) .
    B) ->
    C) *
    D) &

    Answer: A

  3. Which operator is used with a pointer to a structure?

    A) .
    B) ->
    C) ::
    D) %

    Answer: B

  4. What is shared by members of a union?

    A) Different arrays
    B) Same memory location
    C) Different functions
    D) Different files

    Answer: B

  5. Which keyword creates a type alias?

    A) alias
    B) typedef
    C) rename
    D) type

    Answer: B

  6. 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

Problem 1
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

🎯 Remember:

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
Source Code

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
💡 A macro is handled by the preprocessor before compilation. It is not a typed variable.

15.9 Function-like Macro

A macro can accept arguments.

#define SQUARE(x) ((x) * (x))
printf("%d", SQUARE(5));
25

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));
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.

⚠️ Prefer functions when you need predictable argument evaluation and type checking.

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
Normal mode

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
🎯 Header guards are commonly written using #ifndef, #define, and #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;
}
30
10

15.25 Header File Structure

Project

├── 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.

⚠️ #pragma directives are generally compiler-specific. Prefer portable techniques such as traditional include guards when portability is important.

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

📌 Preprocessor → Processes source before compilation.

📌 #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

  1. Which symbol begins a preprocessor directive?

    A) $
    B) #
    C) @
    D) %

    Answer: B

  2. Which directive includes a header file?

    A) #define
    B) #include
    C) #ifdef
    D) #undef

    Answer: B

  3. Which directive defines a macro?

    A) #define
    B) #include
    C) #if
    D) #endif

    Answer: A

  4. Which directive removes a macro definition?

    A) #remove
    B) #delete
    C) #undef
    D) #clear

    Answer: C

  5. Which directive checks whether a macro is defined?

    A) #ifdef
    B) #ifndef
    C) #check
    D) #defined

    Answer: A

  6. Which directive checks whether a macro is not defined?

    A) #ifdef
    B) #ifndef
    C) #notdefined
    D) #ifnot

    Answer: B

  7. Which header is commonly used for printf()?

    A) string.h
    B) stdio.h
    C) math.h
    D) stdlib.h

    Answer: B

  8. Which directive ends a conditional compilation block?

    A) #end
    B) #endif
    C) #stop
    D) #finish

    Answer: B

15.33 Practice Problems

Problem 1
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

🎯 Remember:

#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.

Program

File

Data stored permanently
💡 File handling is useful for storing student records, employee information, marks, reports, logs and other data that should remain available after the program ends.

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);
📌 Always close files that your program has successfully opened.

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:

Name: Venu
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:

ABC

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);
}
📌 Use an int variable for the result of fgetc() when checking for EOF.

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;
}
📌 Opening a file with "a" does not normally erase the existing contents.

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");
⚠️ Be careful with "w" mode because existing file contents can be replaced.

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.

Start

[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

📌 FILE * → File pointer

📌 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

  1. Which type is used for a file pointer?

    A) FILE *
    B) file *
    C) FilePointer
    D) pointer_file

    Answer: A

  2. Which function opens a file?

    A) open()
    B) fopen()
    C) fileopen()
    D) create()

    Answer: B

  3. Which function closes a file?

    A) close()
    B) fclose()
    C) fileclose()
    D) endfile()

    Answer: B

  4. Which mode opens a file for reading?

    A) "r"
    B) "w"
    C) "a"
    D) "x"

    Answer: A

  5. Which mode can erase existing contents when opening a file?

    A) "r"
    B) "w"
    C) "a"
    D) "rb"

    Answer: B

  6. Which mode is used to append data?

    A) "r"
    B) "w"
    C) "a"
    D) "x"

    Answer: C

  7. Which function writes formatted data to a file?

    A) printf()
    B) fprintf()
    C) fwrite()
    D) fput()

    Answer: B

  8. Which function reads one character from a file?

    A) fgets()
    B) fscanf()
    C) fgetc()
    D) fread()

    Answer: C

  9. Which function writes binary blocks?

    A) fwrite()
    B) fprintf()
    C) fputs()
    D) fputc()

    Answer: A

  10. Which function changes the file position?

    A) ftell()
    B) fseek()
    C) rewindfile()
    D) move()

    Answer: B

16.33 Practice Problems

Problem 1
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

🎯 Remember:

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.

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.

Stack Local variables Function call information
Heap Dynamic memory malloc / calloc / realloc

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;
}
10 20 30 40 50

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;
}
0 0 0 0 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;
}
10 20 30 40 50

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

Pointer Variable ptr
Heap Memory [10] [20] [30] [40] [50]

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;
}
📌 Always check the result of malloc(), calloc() and realloc() before using the allocated memory.

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

arr
[10] [20] [30] [40]

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;
}
Hello

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;
⚠️ If dynamically allocated memory is no longer needed, release it using free().

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);
⚠️ Never read from or write through a pointer after its allocated object has been freed.

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

Declare Pointer

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

📌 malloc() → Allocates memory

📌 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

  1. Which function allocates dynamic memory?

    A) malloc()
    B) allocate()
    C) memory()
    D) new()

    Answer: A

  2. Which function releases dynamically allocated memory?

    A) delete()
    B) remove()
    C) free()
    D) release()

    Answer: C

  3. Which function allocates and zero-initializes the allocated bytes?

    A) malloc()
    B) calloc()
    C) realloc()
    D) free()

    Answer: B

  4. Which function changes the size of an allocation?

    A) malloc()
    B) calloc()
    C) realloc()
    D) resize()

    Answer: C

  5. Where is dynamically allocated storage commonly obtained from?

    A) Heap
    B) Register
    C) Code segment
    D) Preprocessor

    Answer: A

  6. What should be checked after malloc()?

    A) EOF
    B) NULL
    C) void
    D) zero

    Answer: B

  7. 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

  8. 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

  9. Which function is declared in stdlib.h?

    A) malloc()
    B) printf()
    C) strlen()
    D) strcpy()

    Answer: A

  10. 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

Problem 1
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

🎯 Remember:

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.

Command Line

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:

argc = 3

Why 3?

Program name → 1
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
argv[0] program.exe
argv[1] 10
argv[2] 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
Argument count = 4

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.

📌 If you need numeric calculations, convert the argument string to a number first.

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:

num = 25

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
Sum = 30

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
Sum = 100

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
Hello 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]
Venu Gopal

18.19 Command Line Argument Diagram

Command Line program.exe 10 20
argc 3
argv[0] program.exe
argv[1] 10
argv[2] 20

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;
}
⚠️ Never access argv[1], argv[2], etc. unless the corresponding argument is known to exist.

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
Result = 30

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

📌 argc → Number of command-line arguments, including the program name

📌 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

  1. What does argc represent?

    A) Argument count
    B) Argument character
    C) Array count
    D) Character count

    Answer: A

  2. What does argv represent?

    A) Argument vector
    B) Argument value
    C) Array variable
    D) Automatic variable

    Answer: A

  3. Which argument normally contains the program name?

    A) argv[0]
    B) argv[1]
    C) argv[2]
    D) argc

    Answer: A

  4. If a program is run as:

    program.exe 10 20

    what is argc?

    A) 2
    B) 3
    C) 10
    D) 20

    Answer: B

  5. What type of data does argv contain?

    A) Integer values
    B) Character strings
    C) Floating-point values only
    D) Structures

    Answer: B

  6. Which function can convert a string to int?

    A) atoi()
    B) itoa()
    C) stringtoint()
    D) convert()

    Answer: A

  7. Which function provides more robust integer conversion and error handling?

    A) atoi()
    B) strtol()
    C) printf()
    D) scanf()

    Answer: B

  8. Which header declares atoi() and strtol()?

    A) stdio.h
    B) stdlib.h
    C) string.h
    D) math.h

    Answer: B

  9. Which should be checked before using argv[2]?

    A) EOF
    B) argc
    C) NULL
    D) sizeof

    Answer: B

  10. 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

Problem 1
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

🎯 Remember:

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.

Decimal Number

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.

1
0
1
0
0
1
1
0

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.

5 00000101
3 00000011
5 & 3 00000001
int result = 5 & 3;

printf("%d", result);
1

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.

5 00000101
3 00000011
5 | 3 00000111
int result = 5 | 3;

printf("%d", result);
7

19.7 Bitwise XOR (^)

XOR produces 1 when the corresponding bits are different.

5 00000101
3 00000011
5 ^ 3 00000110
int result = 5 ^ 3;

printf("%d", result);
6

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.

📌 Remember:

~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);
5 00000101
5 << 1 00001010
10

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);
20 00010100
20 >> 2 00000101
5

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
Odd

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
Bit 2 is SET

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:

0 → 1
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.

1u << 3 00001000

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
Set bits = 3

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:

1 = 0001
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 ^ A = 0

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);
4

Because:

1 ^ 1 = 0
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
⚠️ Do not confuse & with &&, or | with ||.

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

📌 & → AND

📌 | → 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

  1. Which operator performs bitwise AND?

    A) &
    B) &&
    C) |
    D) ||

    Answer: A

  2. Which operator performs bitwise XOR?

    A) &
    B) ^
    C) ||
    D) ~

    Answer: B

  3. Which operator flips bits?

    A) &
    B) |
    C) ^
    D) ~

    Answer: D

  4. What is 5 & 3?

    A) 1
    B) 2
    C) 7
    D) 8

    Answer: A

  5. What is 5 | 3?

    A) 1
    B) 6
    C) 7
    D) 8

    Answer: C

  6. What is 5 ^ 3?

    A) 1
    B) 6
    C) 7
    D) 8

    Answer: B

  7. Which operator performs left shift?

    A) <<
    B) >>
    C) &
    D) ^

    Answer: A

  8. Which expression checks whether bit k is set?

    A) n & (1u << k)
    B) n | k
    C) n ^ k
    D) n + k

    Answer: A

  9. Which expression sets bit k?

    A) n &= (1u << k)
    B) n |= (1u << k)
    C) n ^= ~(1u << k)
    D) n >>= k

    Answer: B

  10. 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

Problem 1
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

🎯 Remember:

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;
}
1

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;
}
1

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;
}
Wednesday

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;
}
GO

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 names declared inside an enum are integer constants.

📌 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;
}
100

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;
}
ID = 101
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.

⭐ This is one reason typedef can make pointer declarations easier to read.

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;
}
30

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;
}
ID = 101
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

📌 enum → Named integer constants

📌 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

  1. What is enum used for?

    A) Named integer constants
    B) Dynamic memory
    C) File handling
    D) Pointer arithmetic

    Answer: A

  2. What is the default value of the first enum constant?

    A) 0
    B) 1
    C) -1
    D) Undefined

    Answer: A

  3. Which keyword creates a type alias?

    A) alias
    B) typedef
    C) type
    D) define

    Answer: B

  4. Does typedef create a new variable?

    A) Yes
    B) No
    C) Only for pointers
    D) Only for structures

    Answer: B

  5. Which is commonly used with typedef?

    A) Structure
    B) goto
    C) switch only
    D) printf

    Answer: A

  6. Which performs preprocessor text substitution?

    A) typedef
    B) #define
    C) enum
    D) struct

    Answer: B

  7. 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

  8. 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

  9. Can typedef be used with a function pointer?

    A) Yes
    B) No
    C) Only in C++
    D) Only with void functions

    Answer: A

  10. 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

Problem 1
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

🎯 Remember:

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:

5 × 2 = 10
10 + 10 = 20
Result = 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:

20 / 5 = 4
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
📌 Higher position in the table means higher precedence.

📌 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
20
(10 + 5) * 2
30

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;
20 × 3 = 60
10 + 60 = 70
70

21.6 Modulus with Other Operators

int x = 20 + 15 % 4;
15 % 4 = 3
20 + 3 = 23
23

21.7 Left-to-Right Associativity

100 / 10 * 2

Both / and * have the same precedence and associate left-to-right.

100 / 10 = 10
10 × 2 = 20
20

21.8 Subtraction Example

20 - 5 - 3

The - operator associates from left to right.

20 - 5 = 15
15 - 3 = 12
12

21.9 Relational Operators

Relational operators have lower precedence than arithmetic operators.

int result = 10 + 5 > 12;
10 + 5 = 15
15 > 12
true → 1
1

21.10 Equality vs Relational Operators

Relational operators have higher precedence than equality operators.

int result = 5 < 10 == 1;
5 < 10 → 1
1 == 1 → 1
1

21.11 Logical Operator Precedence

The order among the common logical operators is:

!

&&

||
1 || 0 && 0

First evaluate AND:

0 && 0 = 0
1 || 0 = 1
1

21.12 Logical NOT

Logical NOT has higher precedence than logical AND.

!0 && 1
!0 = 1
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
5

21.14 Shift vs Arithmetic Operators

Shift operators have lower precedence than addition and subtraction.

1 << 2 + 1

Addition is evaluated first:

2 + 1 = 3
1 << 3 = 8
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.

20 × 2 = 40
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:

c = 10
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.

x = 6
a = 6

Post-increment:

int x = 5;

int a = x++;
a = 5
x = 6

21.19 Pre-decrement vs Post-decrement

int x = 5;

int a = --x;
x = 4
a = 4
int x = 5;

int a = x--;
a = 5
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)++
📌 Use parentheses when pointer expressions could be confusing.

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.

10

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)
📌 Even though this is valid, nested conditional expressions should be written clearly with parentheses.

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.

x = 30

21.26 Tricky Expression 1

int x = 10 + 20 * 3;
20 × 3 = 60
10 + 60 = 70
x = 70

21.27 Tricky Expression 2

int x = 20 / 5 * 2;
20 / 5 = 4
4 × 2 = 8
x = 8

21.28 Tricky Expression 3

int x = 10 + 5 > 12;
10 + 5 = 15
15 > 12 = 1
x = 1

21.29 Tricky Expression 4

int x = 5 | 3 & 1;
3 & 1 = 1
5 | 1 = 5
x = 5

21.30 Tricky Expression 5

int x = 1 << 2 + 1;
2 + 1 = 3
1 << 3 = 8
x = 8

21.31 Tricky Expression 6

int a = 5;
int b = 10;

int result = a < b && b < 20;
5 < 10 → 1
10 < 20 → 1
1 && 1 → 1
result = 1

21.32 Tricky Expression 7

int a = 5;
int b = 10;

int result = a + b > 10 && b > 5;
5 + 10 = 15
15 > 10 → 1
10 > 5 → 1
1 && 1 → 1
result = 1

21.33 Precedence Does NOT Determine Evaluation Order

⚠️ This is extremely important.

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;
⭐ Good C programming prefers clear and well-defined expressions over clever but confusing expressions.

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

📌 Precedence → Determines how operators are grouped.

📌 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

  1. Which operator has higher precedence?

    A) +
    B) *
    C) =
    D) ||

    Answer: B

  2. What is the associativity of multiplication?

    A) Left-to-right
    B) Right-to-left
    C) Top-to-bottom
    D) None

    Answer: A

  3. Which has higher precedence?

    A) ==
    B) <
    C) =
    D) ||

    Answer: B

  4. What is the result of 10 + 5 * 2?

    A) 30
    B) 20
    C) 25
    D) 15

    Answer: B

  5. What is the result of 20 / 5 * 2?

    A) 2
    B) 8
    C) 10
    D) 20

    Answer: B

  6. Which operator has lower precedence than ==?

    A) +
    B) *
    C) &&
    D) <

    Answer: C

  7. Which operator associates right-to-left?

    A) +
    B) *
    C) =
    D) %

    Answer: C

  8. What is the result of 1 << 2 + 1?

    A) 5
    B) 6
    C) 8
    D) 9

    Answer: C

  9. What is *p++ interpreted as?

    A) (*p)++
    B) *(p++)
    C) *(++p)
    D) *p + 1

    Answer: B

  10. Which operator has the lowest precedence?

    A) +
    B) &&
    C) =
    D) comma

    Answer: D

21.40 Practice Problems

Problem 1
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

🎯 Remember:

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;
}
10

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 20
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;
}
0 1 2 3 4

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;
}
1
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

First call → count = 1
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:

0

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 1 times
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;
}
1 2 3

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;
}
500

22.32 Common Mistakes

  • Thinking static always means "global".
  • Thinking register guarantees CPU-register storage.
  • Reading an uninitialized automatic local variable.
  • Thinking extern creates a new variable.
  • Confusing scope with lifetime.
  • Forgetting that file-scope static gives internal linkage.
  • Assuming an extern declaration is itself a definition.

22.33 Quick Revision

📌 auto → Default for ordinary local variables.

📌 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

  1. Which storage class is the default for ordinary local variables?

    A) static
    B) auto
    C) extern
    D) register

    Answer: B

  2. Which keyword allows a local variable to retain its value between function calls?

    A) auto
    B) register
    C) static
    D) extern

    Answer: C

  3. Which keyword declares a reference to an object defined elsewhere?

    A) auto
    B) static
    C) register
    D) extern

    Answer: D

  4. 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

  5. An uninitialized static-duration int object is initialized to:

    A) 1
    B) -1
    C) 0
    D) Garbage

    Answer: C

  6. 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

  7. 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

  8. Which concept describes how long an object exists?

    A) Scope
    B) Lifetime
    C) Syntax
    D) Precedence

    Answer: B

  9. File-scope static variables generally have:

    A) External linkage
    B) Internal linkage
    C) No storage
    D) Dynamic linkage

    Answer: B

  10. 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

Problem 1
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

Q1. What is a storage class?

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

🎯 Remember:

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;
}
3.141590

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;
}
25

23.6 Why Parentheses Matter in Macros

Unsafe macro:

#define SQUARE(x) x * x

Consider:

SQUARE(2 + 3)

Expansion:

2 + 3 * 2 + 3
⚠️ Result = 11, not 25.

Correct version:

#define SQUARE(x) ((x) * (x))

Now:

SQUARE(2 + 3)

becomes:

((2 + 3) * (2 + 3))
25

23.7 Macro Arguments and Side Effects

Consider:

#define SQUARE(x) ((x) * (x))

int i = 5;

int result = SQUARE(i++);
⚠️ The argument 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));
Hello

Practical Example

#define SHOW(x) printf("%s = %d\n", #x, x)

int age = 20;

SHOW(age);
age = 20

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
Debug mode enabled

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
💡 Include guards are commonly used in reusable C header files.

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
Version 2

23.20 #else

#define DEBUG 0

#if DEBUG

printf("Debug mode");

#else

printf("Normal mode");

#endif
Normal mode

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));
20

23.33 Macro Function for Minimum

#define MIN(a,b) ((a) < (b) ? (a) : (b))
printf("%d", MIN(10, 20));
10

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);
a = 20
b = 10

23.35 Macro Pitfall: Operator Precedence

Bad macro:

#define DOUBLE(x) x + x

Consider:

3 * DOUBLE(4)

Expansion:

3 * 4 + 4
⚠️ Result = 16

Correct macro:

#define DOUBLE(x) ((x) + (x))
3 * DOUBLE(4) → 24

23.36 Macro Pitfall: Side Effects

#define SQUARE(x) ((x) * (x))

Avoid:

SQUARE(i++);
⚠️ The expression may be evaluated more than once. Prefer a normal function when appropriate.

23.37 Macro Pitfall: Missing Parentheses

Bad:

#define ADD(a,b) a + b
10 * ADD(2,3)

Expansion:

10 * 2 + 3
⚠️ Result = 23

Correct:

#define ADD(a,b) ((a) + (b))
10 * ADD(2,3) → 50

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;
}
ID: 101
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 #ifdef instead of #if.
  • Forgetting that #undef removes 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

🎯 The preprocessor runs before compilation.

🎯 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.

🚀 Welcome to C Programming Practice!

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.

© 2026 Venu Learning