0% found this document useful (0 votes)
22 views285 pages

Gate Notes Realted To Programming

The document explains storage classes in C programming, detailing characteristics such as storage area, default value, scope, and lifetime of variables. It covers types of storage classes including automatic, static, external, and register, along with their memory allocation and usage. Additionally, it describes memory sections like static, heap, and stack, emphasizing their roles in variable storage and management.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
22 views285 pages

Gate Notes Realted To Programming

The document explains storage classes in C programming, detailing characteristics such as storage area, default value, scope, and lifetime of variables. It covers types of storage classes including automatic, static, external, and register, along with their memory allocation and usage. Additionally, it describes memory sections like static, heap, and stack, emphasizing their roles in variable storage and management.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 285

6.2.1.1 – 2.

1
Key Concepts of Storage Classes:

1. Introduction to Storage Classes:

o Storage classes describe the characteristics of variables in a program, such as their


storage area, default value, scope, and lifetime.

o They help in understanding where variables are stored in memory, how long they
persist, and how they are accessed.

Characteristics of Variables:

1. Storage Area:

o Variables are stored in different memory sections: static memory, stack memory, or
heap memory.

o The location where the variable is stored is crucial for understanding how memory is
allocated.

2. Default Value:

 Variables may be initialized with a default value. There are two possibilities:

o Garbage value: If a variable is not explicitly initialized, it may hold random or


"garbage" data.

o Zero: In certain cases (e.g., static variables), the default value is 0.

3. Scope:

 The scope defines the region of the program where the variable is accessible.

 Variables can have:

o Function scope: Limited to the function in which they are declared.

o Program scope: Accessible across the entire program (e.g., global variables).

Example:

int I = 10; // Global variable


void abc() {
int I = 20; // Local variable to abc()
}
int main() {
printf("%d", I); // Accesses the global variable I
}
4. Lifetime:

 Lifetime refers to how long the variable exists in memory.

 Variables in different scopes have different lifetimes:

o Function scope: Created when the function is entered, and destroyed when the
function exits.

o Global variables: Exist for the duration of the entire program.

Types of Storage Classes in C:


1. Automatic (auto) Storage Class:
o Variables declared inside a function or block by default have automatic storage.

o They are stored in the stack memory and have function scope and a garbage default
value.

o Storage Duration: Automatic (created when the block is entered, destroyed when
exited).

o Scope: Local to the block or function in which they are declared.

o Memory Storage: Stored in the stack segment.

o Initialization: Not initialized by default; contains garbage values if not explicitly


initialized.

o EXAMPLE:

int x = 10; // Implicitly 'auto' storage class

auto int y = 20; // Explicitly 'auto' storage class

int z; // Uninitialized automatic variable

2. Static Storage Class:


o Variables with the static keyword retain their value even after the function exits.

o Static variables are stored in the static memory section, specifically in the data
section.

o Their lifetime lasts for the duration of the program, but their scope can still be limited
to a function.

o Outside a function (global scope): Static variables are limited to file scope, which
means they cannot be accessed from other files, even though they behave like global
variables inside the file.

o Example:

#include <stdio.h>
void exampleFunction() {

static int count = 0; // Static variable initialization

count++;

printf("The function has been called %d times.\n", count);

int main() {

exampleFunction(); // First call

exampleFunction(); // Second call

exampleFunction(); // Third call

return 0;

Output:

The function has been called 1 times.

The function has been called 2 times.

The function has been called 3 times.

Explanation:

 Static Variable Declaration:

static int count = 0;


o The static keyword before the variable count declares it as a static variable.

o This means count is initialized only once, and its value persists between function
calls.

o Even though count is declared inside exampleFunction, its scope is limited to this
function (it's not accessible outside), but its lifetime extends for the entire duration
of the program.

 Variable Persistence:

o Each time exampleFunction() is called, the variable count retains its value from the
previous call.

o The count++ statement increments the value of count by 1 on each call.

 Output Explanation:

o On the first call, count is 0 (initialized once), then incremented to 1.


o On the second call, count retains the value 1, then incremented to 2.

o On the third call, count is 2, then incremented to 3.

Key Points Demonstrated:

 Retention of Value:
 The static variable count retains its value even after exampleFunction
exits.
 Storage in Static Memory Section:
 The variable is stored in the data segment, a part of the static
memory area reserved for initialized static variables.
 Lifetime vs. Scope:

 Lifetime: The variable exists for the entire duration of the program.

 Scope: Despite its extended lifetime, count is only accessible within


exampleFunction.

3. External (extern) Storage Class:

o Global variables or variables declared with the extern keyword are stored in the
static memory section.
o They are initialized with a default value of 0 and have a program-wide scope.
o EXAMPLE 1:
// main.c
#include <stdio.h>

int globalVariable = 10; // Global variable definition

void displayGlobalVariable();

int main() {
printf("In main.c, globalVariable = %d\n", globalVariable);
displayGlobalVariable();
return 0;
}
o EXAMPLE 2:
// helper.c
#include <stdio.h>

extern int globalVariable; // Declaration of external variable

void displayGlobalVariable() {
printf("In helper.c, globalVariable = %d\n", globalVariable);
}
4. Register Storage Class:
o This suggests to the compiler that the variable should be stored in a CPU register for
faster access.
o Register variables have function scope and lifetime but may not always be stored in
registers due to hardware limitations.
o Register Variable Declaration:
 register int i; // Suggest storing 'i' in a
CPU register
The register keyword suggests to the compiler to store i in a
CPU register.
 Aims to enhance performance by speeding up access to i.
o Function Scope and Lifetime:
 i has scope limited to the countDown function.
 Its lifetime is the duration of the countDown function execution.
 Similar to an automatic (auto) variable in terms of scope and
lifetime.
o Compiler Optimization:
 Modern compilers optimize variable storage automatically.
 The register keyword is often ignored, and the compiler decides the
best storage location.
Memory Sections in C:
1. Static Memory:
 Static memory is divided into:
o Data section: Stores all global variables, static variables, string constants, and
constants.
o Code section: Stores the compiled machine code (the program’s instructions).
 Global and static variables are stored here, and static memory is allocated during
compile time.
 UPDATE :
o Text Segment (Code Segment):
 The text segment contains the compiled program's executable
instructions (machine code).
 It is loaded into memory when the program starts and typically
does not change uring execution (hence static).
 It is marked as read-only to prevent modifications, and all
functions, including both user-defined and system-defined, are
placed here.
 When the CPU executes a program, it fetches instructions from
this code segment.
 For example, the code for printf("%d", k) is stored in this
segment.
 The CPU reads and executes the instructions stored in this
section.
o Data Segment:

This is further divided into two parts:


 Initialized Data Segment: Holds global and static variables that are
initialized before the program starts execution. For example, int x
= 10; would be stored here.
 Uninitialized Data Segment (BSS): Stores global and static
variables that are uninitialized (or initialized to zero). For example,
int y; would be stored in the BSS segment.

2. Heap Memory:
 Used for dynamic memory allocation (e.g., when you use malloc() or calloc()).
 array = (int *)malloc(n * sizeof(int));
o malloc(n * sizeof(int)):
 Allocates a memory block of n * sizeof(int) bytes.
 sizeof(int) ensures portability across different systems.
 Memory allocated with malloc() comes from the heap.
 Allows for flexible memory management at runtime.
 It is allocated during runtime and stores dynamic data structures.
 Memory from the heap can be explicitly deallocated using the free() function.

3. Stack Memory:
 Stores local variables, function parameters, temporaries, and the return address of
function calls.
 Stack memory is created during runtime and destroyed when the function call is
complete.
 UPDATE:
o Whenever a function is called, a new stack frame is created, which stores:
 Function arguments (e.g., "%d" and k in the case of printf).
 Local variables.
 Return addresses (where to go back to once the function call is
complete).
o During function execution, the CPU manages the stack to keep track of active
functions, and execution flows by pushing and popping stack frames.
+-----------------------+
| Code | <-- Text Segment (contains program code)
+-----------------------+
| Initialized Data | <-- Static Memory Section (initialized global/static variables)
+-----------------------+
| Uninitialized Data | <-- Static Memory Section (uninitialized global/static variables, BSS)
+-----------------------+
| |
| Heap | <-- Dynamically allocated memory
+-----------------------+
| Stack | <-- Local variables, function call information
+-----------------------+

Memory Allocation Timing:


 Stack memory is allocated during runtime.
 Heap memory is also allocated during runtime using functions like malloc().
 Static memory is allocated during compile time (or load time).
Auto Storage Class in C Programming –( 6.2.2.1 - 2.2)

1. Syntax to Declare Auto Storage Class:


The syntax for declaring an auto storage class variable is:

 auto data_type variable_name;


However, using the auto keyword is optional, as local variables are auto by default.

2. Default Value of Auto Variable:


Auto variables hold garbage values by default if not initialized. This garbage value is an
unknown value and cannot be predicted.
3. Storage Area of Auto Variables:
Auto variables are stored in the stack memory. This is important because the stack is a
memory structure that follows a last-in, first-out (LIFO) principle, which is crucial for
function execution.
Each function call gets its own stack frame, and the auto variables are stored within this
frame.
4. Scope
The scope of an auto variable is local to the block in which it is declared. It cannot be
accessed outside of that block or function.
5. Lifetime of Auto Variables:
The lifetime of auto variables is bound to the execution of the function. They are created
when the function is called and deleted when the function exits.

Example Program:
Let's consider the following C program to illustrate these concepts:
Program Output
When you run this program, the output will be:
15
15
5

Visual Representation of Memory Organization


Before the First Call to abc(i);
Stack:
( Return Address | <-- Return address for when `main` exits, SIMILARYLY OTHER
FUNCTIONS ALSO HAS RETURN ADDRESS IN THIS CASE WJEN ABC EXITS)
 The main function's stack frame is created.
 Local variable i is stored in the stack frame with a value of 5.
During the First Call to abc(i);
Stack:

(Abc() function is being called below main(), as new function


calls in the stack grow downward)####
 abc is called with m = 5.
 A new stack frame for abc is created on top of main's stack frame.
 Local variable k is initialized to 10.
 k is updated to k + m, so k = 10 + 5 = 15. (WHRE DOES THIS ADDITION
PROCESS TAKE PLACE ???)
 15 is printed.
 Upon completion, abc's stack frame is destroyed.

After the First Call to abc(i);


Stack:

 Only main's stack frame remains.

During the Second Call to abc(i);


The same steps as the first call:
 abc is called again with m = 5.
 A new stack frame for abc is created.
 k is re-initialized to 10.
 k is updated to k + m, so k = 10 + 5 = 15.
 15 is printed.
 Upon completion, abc's stack frame is destroyed.
After the Second Call to abc(i);
 Only main's stack frame remains.

Printing i in main
 printf("%d\n", i); prints 5.

Program Termination
 main returns.
 main's stack frame is destroyed.
 The program ends.

Recursion Tree Structure


 The idea is to represent the recursive function calls as a tree structure to help
visualize the order in which function calls happen and how the control returns once
the recursion ends.

FROM GPT UPDATES:


Understanding Stack Frames
 Stack Frames (Activation Records):
o Each function call creates a new frame on the stack.
o Contains:
 Function parameters.
 Local variables.
 Return address (not shown in diagrams).
 Possibly saved registers and alignment padding.
 Example Stack Frame for abc:

 Stack Growth:
o On many systems, the stack grows downward (from higher to lower memory
addresses).

25- 9 -2024

Logical View (Abstract Concept of a Stack)


In programming, we think of the stack as growing upward logically as new function calls are
made. That is:
 Each new function call is pushed onto the stack.
 When the function completes, it is popped from the stack.
 The most recent function call is considered at the "top" of the stack.
This is a common way to conceptualize function calls and their organization in a stack.

Physical Memory Growth (Memory Address Space)


However, in many computer architectures (e.g., x86 and others), the physical memory
addresses that represent the stack may grow downward as new function calls are made.
Here’s what happens:
 When a new function is called, a stack frame (containing local variables, return
addresses, etc.) is allocated in memory.
 In these architectures, the newer frames are stored at lower memory addresses
(the stack pointer decreases), which is why we say the stack grows downward in
memory.

Key Point:
 Conceptually/logically: We visualize the stack as growing upward, where the "top"
of the stack holds the most recent function call.
 Physically (in some architectures): Even though the stack grows logically "upward,"
the memory addresses might actually decrease (grow downward) as new stack
frames are added.
This behavior is specific to how hardware manages the stack in memory, but it doesn't affect
how we think of the stack in terms of function calls in our code.
Example:
Consider this scenario in an x86 system where the stack grows downward in memory:
 The stack might start at a high memory address like 0x7fff0000.
 As a new function is called, the stack pointer moves to a lower address like
0x7ffeff00.
 A subsequent function call pushes the stack pointer further down to 0x7ffef000.
Even though the memory addresses are decreasing (downward), we still think of the stack
logically as growing upward (new frames on top of old ones).
Understanding Static Variables in C – (6.2.3.1-2.3)

Characteristics of Static Variables


 Default Value: If you do not initialize a static variable, it defaults to zero.
 Scope: A static variable declared inside a function has function scope. It is accessible
only within that function.
 Lifetime: The lifetime of a static variable is the entire duration of the program. It is
initialized only once and retains its value between function calls.
 Storage Area: Static variables are stored in the data segment of the program's
memory (static memory), not on the stack.

Example Program
Let's examine a program to understand how static variables work:
Initial Memory State Before Execution
Data Segment (Static Memory):

Static Variable k:
o Allocated in the initialized data segment because it is a static variable
initialized to 10.
Stack Segment:

 No function calls have been made yet, so the stack is empty.

Step-by-Step Execution and Memory Visualization


1. Program Start: Entering main
Stack:

 Function main is called.


 Local variable i is allocated on the stack and initialized to 5.
Data Segment remains unchanged:

2. First Call to abc(i)


 abc(i) is called with m = 5.
Stack:

 A new stack frame for abc is created.


 Parameter m is allocated on the stack with value 5.

Data Segment Update:


 Before Update:

Computation in abc:
 k = k + m → k = 10 + 5 → k = 15.
o After Update:

o Output:
Prints 15.

Stack after Function Returns:

 Stack frame for abc is destroyed after returning.

3. Second Call to abc(i)


 abc(i) is called again with m = 5.
Stack:

 A new stack frame for abc is created again.


 Parameter m is allocated on the stack with value 5.
Data Segment Update:
 Before Update:

Computation in abc:
 k = k + m → k = 15 + 5 → k = 20.
After Update:

Output:
 Prints 20.
Stack after Function Returns:

 Stack frame for abc is destroyed after returning.


4. Back in main: Printing i
 Execution resumes in main.
5. Program Termination
 main function returns.
 Stack is cleared.
Final Memory State:
 Stack:

 Static variable k remains allocated until the program exits.

Auto and Static storage class exercises


EX1

Note: The second printf("%d", a); will execute after the recursive call returns.

Recursion Tree Visualization:

Final Output: 234444


Detailed Explanation of the Recursion Tree:
Level 1 (Root of the Tree):
 Function Call: main() Level 1
 Static Variable a:
o Initialized to 1
o Incremented: ++a → a = 2
 Output Before Recursion:
o printf("%d", a); → Prints: 2
 Recursive Call Condition:
o if (a <= 3) → 2 <= 3 is True
o Calls main() Level 2

Level 2 (First Child of Level 1):


 Function Call: main() Level 2
 Static Variable a:
o Carries over from Level 1: a = 2
o Incremented: ++a → a = 3
 Output Before Recursion:
o printf("%d", a); → Prints: 3
 Recursive Call Condition:
o if (a <= 3) → 3 <= 3 is True
o Calls main() Level 3

Level 3 (Child of Level 2):


 Function Call: main() Level 3
 Static Variable a:
o Carries over from Level 2: a = 3
o Incremented: ++a → a = 4
 Output Before Recursion:
o printf("%d", a); → Prints: 4
 Recursive Call Condition:
o if (a <= 3) → 4 <= 3 is False
o Does not call main() again
 Output After Recursion:
o printf("%d", a); → Prints: 4
 Returns to Level 2

Returning to Level 2:
 Output After Recursion:
o printf("%d", a); → Prints: 4 (Note: a is still 4)
 Returns to Level 1

Returning to Level 1:
 Output After Recursion:
o printf("%d", a); → Prints: 4
 Program Ends

Alternative RepresentationS:
You can also visualize the recursion tree in terms of function calls and returns:

(1)

(2)
[Level 1] main()
- a = 1 (initial)
- ++a; a = 2
- printf("%d", a); // Output: 2
- if (a <= 3) // 2 <= 3 is True
|
|-- [Level 2] main()
- a = 2 (from Level 1)
- ++a; a = 3
- printf("%d", a); // Output: 3
- if (a <= 3) // 3 <= 3 is True
|
|-- [Level 3] main()
- a = 3 (from Level 2)
- ++a; a = 4
- printf("%d", a); // Output: 4
- if (a <= 3) // 4 <= 3 is False
- printf("%d", a); // Output: 4 (after recursion)
- printf("%d", a); // Output: 4 (after recursion in Level 2)
- printf("%d", a); // Output: 4 (after recursion in Level 1)

Leaf Nodes:
 The deepest recursive call where the condition a <= 3 is False.
 In this case, Level 3 is the leaf node.
Backtracking:
 After reaching the leaf node, the program backtracks (returns) to previous levels.
 Outputs after recursion are printed during this phase.

EX 2
Understanding the Recursive Calls:
Each call to main creates a new stack frame
with its own auto variable a.

Since a is always initialized to 1 and then


incremented to 2, the condition a <= 3 is
always true.

This causes infinite recursion until a stack


overflow occurs, leading to abnormal
termination. (CORRECT OPTION)

Recursion Tree Diagram (GPT):


Call 1: main()
a = 2 (prints 2)
|
V
Call 2: main()
a = 2 (prints 2)
|
V
...
V
Call N: main()
a = 2 (prints 2)
EX 3 : Functions F1 and F2 with Static Variables
Objective: To understand how static variables behave when functions are called multiple
times, and how their initialization affects the output.

Function Definitions
#include <stdio.h>
void F1() {
static int x = 10;
++x;
printf("%d\n", x);
}
void F2() {
static int x;
x = 10;
++x;
printf("%d\n", x);
}
int main() {
F1();
F1();
F2();
F2();
return 0;
}

Explanation
1. Function F1:
o Static Variable Initialization: The static variable x is initialized to 10 only once,
the first time F1 is called.
o First Call to F1:
 x is 10.
 ++x increments x to 11.
 printf outputs 11.
o Second Call to F1:
 x retains its previous value 11 (not reinitialized).
 ++x increments x to 12.
 printf outputs 12.

2. Function F2:
o Static Variable Declaration: The static variable x is declared but not
explicitly initialized, so it defaults to 0.
o Each Call to F2:
 Reassignment: x = 10 resets x to 10 every time F2 is called.
 ++x increments x to 11.
 printf outputs 11.
o Static Variable Behavior: Although x retains its value between calls, it is
overwritten by x = 10 in each call.

Program Output
11
12
11
11
 First two outputs (11 and 12) come from F1.
 Next two outputs (11 and 11) come from F2.
Key Takeaways
 Static Variables:
o Retain their values between function calls.
o Are initialized only once (on the first function call) if an initializer is provided.
 Function F1 demonstrates that the static variable x retains and updates its value
across multiple calls.
 Function F2 shows that reassigning the static variable within the function body
effectively resets its value each time, despite it retaining its state between calls.

Ex 4: Using a Static Variable in a Function Called within a for Loop

Objective: To understand how a static variable behaves when a function is used in all three
components of a for loop: initialization, condition, and increment.
Function Definition
#include <stdio.h>
int F() {
static int num = 7;
return num--;
}
int main() {
for (int i = F(); F(); F()) {
printf("%d\n", F());
}
return 0;
}

Explanation
 Static Variable num:
o Initialized to 7 only once.
o Decremented after each call to F() due to the num-- (post-decrement
operator).
 for Loop Breakdown:
1. Initialization: int i = F();
 First Call to F():
 num is 7.
 Returns 7 (post-decrement, so num becomes 6 after
returning).
 i is assigned 7 (unused in the loop body).
2. Condition Check: F();
 Second Call to F():
 num is 6.
 Returns 6 (decrements num to 5).
 Since the returned value 6 is non-zero (true), the loop
continues.
3. Loop Body: printf("%d\n", F());
 Third Call to F():
 num is 5.
 Returns 5 (decrements num to 4).
 printf outputs 5.
4. Increment: F();
 Fourth Call to F():
 num is 4.
 Returns 4 (decrements num to 3).
5. Condition Check: F();
 Fifth Call to F():
 num is 3.
 Returns 3 (decrements num to 2).
 Condition is true (non-zero).
6. Loop Body: printf("%d\n", F());
 Sixth Call to F():
 num is 2.
 Returns 2 (decrements num to 1).
 printf outputs 2.
7. Increment: F();
 Seventh Call to F():
 num is 1.
 Returns 1 (decrements num to 0).
8. Condition Check: F();
 Eighth Call to F():
 num is 0.
 Returns 0 (decrements num to -1).
 Condition is false (zero), loop exits

Program Output
5
2

Key Takeaways
 Static Variables in Loops:
o Using a function with a static variable in all parts of a for loop demonstrates
the order of execution:
1. Initialization
2. Condition Check
3. Loop Body
4. Increment
 Post-Decrement Operator (num--):
o Returns the current value of num, then decrements it.
o Critical for understanding the values used in the condition and the outputs.
 Control Flow:
o The loop continues as long as the condition returns a non-zero value.
o The decremented num eventually reaches 0, causing the loop to terminate.
General Concepts
Static Variables:
 Function Calls in for Loops:
o Can be used in initialization, condition, and increment sections.
o Execution order is important to predict the output.
Ex 5: Determining the Value of a Variable after a Loop
Question: What is the content of variable J after the termination of the loop?
Program Overview
We have a function incr that accepts an integer argument and uses a static variable count.
The function increments count by the passed argument and returns count. In the main
function, we loop from I = 1 to I <= 4, calling incr(I) each time and storing the result in J.
Code:
#include <stdio.h>
int incr(int I) {
static int count = 0;
count = count + I;
return count;
}
int main() {
int I, J;
for (I = 1; I <= 4; I++) {
J = incr(I);
}
printf("The final value of J is: %d\n", J);
return 0;
}

Explanation
1. Static Variable count:
o Initialization: count is initialized to 0 only once.
o Persistence: Retains its value between function calls.
2. Loop Execution:
o First Iteration (I = 1):
 count = count + I → count = 0 + 1 → count = 1.
 J = incr(1) → J = 1.
o Second Iteration (I = 2):
 count = count + I → count = 1 + 2 → count = 3.
 J = incr(2) → J = 3.
o Third Iteration (I = 3):
 count = count + I → count = 3 + 3 → count = 6.
 J = incr(3) → J = 6.
o Fourth Iteration (I = 4):
 count = count + I → count = 6 + 4 → count = 10.
 J = incr(4) → J = 10.
3. After Loop Termination:
o The final value of J is 10.
Answer
 The content of variable J after the termination of the loop is 10.

EX 6 Evaluating a Recursive Function with a Static Variable


Question: What is the returned value of F(5)?
Program Overview
We have a recursive function F that uses a static variable R. The function updates R when N
> 3 and recursively calls itself
CODE:
#include <stdio.h>
int F(int N) {
static int R = 0;
if (N <= 0)
return 1;
if (N > 3) {
R = N;
return F(N - 1) + 2;
}
return F(N - 2) + R;
}
int main() {
int result = F(5);
printf("The value returned by F(5) is: %d\n", result);
return 0;
}

Function Behavior
 Static Variable R:
o Initialized to 0 only once.
o Updated to N when N > 3.
o Retains its value across recursive calls unless updated.
 Function Logic:
o Base Case: If N <= 0, return 1.
o When N > 3:
 Update R = N.
 Return F(N - 1) + 2.
o When N <= 3 and N > 0:
 Return F(N - 2) + R.

Recursion Tree for F(5)


Let's build the recursion tree step by step.
Initial Call
 Call F(5)
o N=5
o Since N > 3, we update R = 5.
o Compute return F(5 - 1) + 2 → return F(4) + 2
First Level Recursion
 Call F(4)
o N=4
o Since N > 3, we update R = 4.
o Compute return F(4 - 1) + 2 → return F(3) + 2
Second Level Recursion
 Call F(3)
o N=3
o Since N <= 3 and N > 0, we do not update R (remains 4).
o Compute return F(3 - 2) + R → return F(1) + 4
Third Level Recursion
 Call F(1)
o N=1
o Since N <= 3 and N > 0, R remains 4.
o Compute return F(1 - 2) + R → return F(-1) + 4
Base Case
 Call F(-1)
o N = -1
o Since N <= 0, return 1

Unwinding the Recursion


Now, we can compute the return values as we unwind the recursive calls.
1. Return from F(-1):
o Returns 1 (base case)
2. Return from F(1):
o return F(-1) + R → 1 + 4 = 5
3. Return from F(3):
o return F(1) + 4 → 5 + 4 = 9
4. Return from F(4):
o return F(3) + 2 → 9 + 2 = 11
5. Return from F(5):
o return F(4) + 2 → 11 + 2 = 13

Recursion Tree
F(5)
│N=5
│ R updated to 5
│ Returns: F(4) + 2

│ ↳ F(4) returns 11
│ Result: 11 + 2 = 13

└── F(4)
N=4
R updated to 4
Returns: F(3) + 2

│ ↳ F(3) returns 9
│ Result: 9 + 2 = 11

└── F(3)
N=3
R remains 4
Returns: F(1) + 4

│ ↳ F(1) returns 5
│ Result: 5 + 4 = 9

└── F(1)
N=1
R remains 4
Returns: F(-1) + 4

│ ↳ F(-1) returns 1
│ Result: 1 + 4 = 5

└── F(-1)
N = -1
Base case
Returns: 1

Explanation of Static Variable R


 At F(5):
o R is updated to 5
 At F(4):
o R is updated to 4 (overwriting the previous value)
 At F(3) and below:
o R remains 4
Note: The value of R changes during the recursive calls, and its value at the time of each
call affects the computations.
EX 7: Recursive Function count with a Static Variable
Question: What is the output of the following ng program when main calls
count(3)?
Explanation
Let's walk through the execution step by step.
1. Initial Call: count(3)
o N: 3
o D: 1 (static variable, initialized
once)
o Outputs:
 Prints N: 3
 Prints D: 1
o D Incremented: D becomes 2
o Recursive Call: Since N > 1, calls
count(2)
2. First Recursive Call: count(2)
o N: 2
o D: 2 (retained from previous call)
o Outputs:
 Prints N: 2
 Prints D: 2
o D Incremented: D becomes 3
o Recursive Call: Since N > 1, calls count(1)
3. Second Recursive Call: count(1)
o N: 1
o D: 3 (retained from previous call)
o Outputs:
 Prints N: 1
 Prints D: 3
o D Incremented: D becomes 4
o Recursive Call: Since N is not greater than 1, no further recursive calls
o Post-Recursive Output:
 Prints D: 4
4. Returning to count(2)
o D: 4 (updated by the deeper call)
o Post-Recursive Output:
 Prints D: 4
5. Returning to count(3)
o D: 4 (updated by the deeper call)
o Post-Recursive Output:
 Prints D: 4
Program Output
The sequence of printed values is:
3
1
2
2
1
3
4
4
4
EX 8 : Recursive Function F with Static Variable and Conditional
Logic
Question: What is the value returned by F(1)?
Code
#include <stdio.h>
int F(int N) {
static int I = 0;
if (N > 5)
return N;
else {
N = N + I;
I++;
return F(N);
}
}
int main() {
int result = F(1);
printf("%d\n", result);
return 0;
}

Explanation
Let's trace the function calls step by step.
Step 1: Call F(1)
 N: 1
 I: 0
 Update N: N = 1 + 0 = 1
 Increment I: I = 1
 Recursive Call: F(1)
Step 2: Call F(1)
 N: 1
 I: 1
 Update N: N = 1 + 1 = 2
 Increment I: I = 2
 Recursive Call: F(2)
Step 3: Call F(2)
 N: 2
 I: 2
 Update N: N = 2 + 2 = 4
 Increment I: I = 3
 Recursive Call: F(4)
Step 4: Call F(4)
 N: 4
 I: 3
 Update N: N = 4 + 3 = 7
 Increment I: I = 4
 Recursive Call: F(7)
Step 5: Call F(7)
 N: 7
 I: 4
 Condition N > 5: True
 Return: N → Returns 7
Unwinding:
 Each previous call returns 7:
o F(4) returns 7
o F(2) returns 7
o F(1) returns 7

o Initial F(1) returns 7


Global Variables in C Programming
Definition:
 Global variables, also known as external variables, are variables declared outside of
all function blocks.
 They are accessible across all functions in the program, provided there is no local
variable with the same name in a given scope.

Storage Class:
 Global variables have the external storage class by default.
 They are stored in the static memory area, specifically in the data segment of the
program.

Default Values:
 If not explicitly initialized, global variables are automatically initialized to zero.

Scope and Lifetime:


 The scope of a global variable is from its point of declaration to the end of the
program.
 The lifetime of a global variable is the entire execution time of the program (program
lifetime).

Understanding Through an Example Program


Let's consider the following corrected C program to illustrate the concepts:

#include <stdio.h>
int x = 10; // Global variable
// Function prototypes
int f1(void);
int f2(void);
void f3(void);

int main(void) {
int x = 1; // Local variable in main
x = x + f1() + f2() + f3() + f2();
printf("Value of x in main: %d\n", x);
return 0;
}

int f1(void) {
int x = 25; // Local variable in f1
x++; // Increment local x
return x; // Returns 26
}
int f2(void) {
static int x = 50; // Static local variable in f2
x++; // Increment static x
return x; // Returns incremented value
}
void f3(void) {
x = x * 10; // Modifies global x
// No return value as function is void
}
Explanation of the Program:
1. Global Variable x:
o Declared outside all functions with an initial value of 10.
o Accessible in any function that does not have a local variable named x.
2. Local Variables:
o In main():
 Declares a local variable x initialized to 1.
 This local x overshadows the global x within main().
o In f1():
 Declares a local variable x initialized to 25.
 Increments x to 26 and returns it.
o In f2():
 Declares a static local variable x initialized to 50.
 Static variables retain their value between function calls.
 Increments x and returns it.
o In f3():
 Does not declare a local x.
 Modifies the global x by multiplying it by 10.
3. Function Calls in main():
o First f1():
 Returns 26.
o First f2():
 Static x increments from 50 to 51 and returns 51.
o f3():
 Global x changes from 10 to 100.
 Since f3() is void, it does not return a value.
o Second f2():
 Static x increments from 51 to 52 and returns 52.
4. Calculating x in main():
o x = 1 (local x) + 26 (from f1) + 51 (from first f2) + 0 (f3 is void) + 52 (from
second f2)
o Total: x = 1 + 26 + 51 + 0 + 52 = 130
5. Output:
o The program prints: Value of x in main: 130
Key Points:
 Local vs. Global Variables:
o If a local variable has the same name as a global variable, the local variable
takes precedence within its scope.
o In main() and f1(), the local x is used instead of the global x.
 Static Local Variables:
o Declared with the static keyword inside a function.
o Retain their value between function calls.
o Stored in the static memory area, not on the stack.
 Global Variable Modification:
o Functions that do not have a local variable with the same name can access
and modify the global variable.
o In f3(), since there is no local x, it modifies the global x.
 Function Prototypes:
o Declaring function prototypes before main() ensures that the compiler knows
about the functions used later in the code.
 Order of Function Calls:
o Function calls are evaluated from left to right.
o The state of variables (especially static and global variables) can change
between calls.
Memory Allocation:
 Static Memory Area:
o Stores global variables, static local variables, and static functions.
o Exists throughout the program's execution.
 Stack Memory:
o Stores local variables and function call information.
o Variables are created when the function is called and destroyed when the
function exits.
Global Variables in Multiple Files

 Definition: Global variables are variables declared outside of any function block and
are accessible throughout the program.
 Problem: When global variables are defined in one file but used in another, the
compiler needs to know about their existence and type during compilation.

Compilation and Linking Process


1. Compilation:
o Each .c source file is compiled independently into an object file (.o or .obj).
o The compiler checks for syntax errors and verifies that all variables and
functions used are declared.
2. Linking:
o Object files are linked together to create the final executable.
o The linker resolves symbol references between object files.

The Issue Without extern


If you use a global variable in a file where it is not defined, and you have not declared it
using extern, the compiler will raise a compile-time error because it doesn't know about
the variable's existence or type.

Using the extern Keyword


 Purpose: The extern keyword tells the compiler that the variable is defined
elsewhere, possibly in another file.
 Syntax: extern data_type variable_name;
Example Scenario
Suppose we have two files:
1. globals.c (Defines global variables):

2. main.c (Uses global variables):

#include <global.c>

Explanation of the Example


 In globals.c:
o Global variables a and b are defined and initialized.
o These variables are stored in the static memory area (data segment).
 In main.c:
o The extern keyword is used to declare that a and b are global variables
defined elsewhere.
o The compiler now knows the types of a and b and allows their usage in
main.c.
Compilation Steps
1. Compile globals.c:

2. Compile main.c:

o Generates main.o (object file).


o No compile-time errors since a and b are declared with extern.
3. Link Object Files:

 Links globals.o and main.o to create the executable program.


 The linker resolves the references to a and b.

Linker Error
 Cause: Declaring a variable with extern but not defining it in any of the linked files.
 Example:
o main.c:
#include <stdio.h>
extern int a;
extern int b;
int main(void) {
int c = 20;
c = a + b;
printf("Value of c: %d\n", c);
return 0;
}
 No globals.c file or a and b are not defined anywhere
Defining Global Variables After main()
 Use extern to declare the global variable before main().
extern int a;
int main(void) {
a = 10;
printf("Value of a: %d\n", a);
return 0;
}
int a; // Definition of global variable

Global Variable Exercises


EX – 1
FIND OUTPUT OF CODE
#include <stdio.h>
int A, B, C; // Global variables
void F() {
static int A = 2; // Static local variable in F
int B = 1; // Local variable in F
A = A + (++B);
printf("%d %d ", A, B);
}
int main() {
static int A = 1; // Static local variable in main
F();
A = A + 1;
F();
printf("%d %d\n", A, B); // B here refers to the global variable B
return 0;
}

Program Execution Steps:


1. Global Variables Initialization:
o int A, B, C; are declared globally and initialized to 0 by default.
2. First Call to F():
o Static Variable A in F:
 Initialized to 2 only once when the program starts.
 Current value: A = 2.
o Local Variable B in F:
 Initialized to 1 each time F() is called.
 Current value: B = 1.
o Expression A = A + (++B);:
 ++B increments B before using it in the expression.
 B becomes 2.
 A = 2 + 2 = 4.
o Output:
 printf("%d %d ", A, B); prints 4 2 .
3. In main() After First Call:
o Static Variable A in main:
 Initialized to 1 only once when the program starts.
 Current value: A = 1.
o Expression A = A + 1;:
 A = 1 + 1 = 2.
4. Second Call to F():
o Static Variable A in F:
 Retains its value from the previous call.
 Current value: A = 4 from the first call.
o Local Variable B in F:
 Reinitialized to 1.
 Current value: B = 1.
o Expression A = A + (++B);:
 ++B increments B to 2.
 A = 4 + 2 = 6.
o Output:
 printf("%d %d ", A, B); prints 6 2 .
5. In main() After Second Call:
o Static Variable A in main:
 Current value: A = 2.
o Global Variable B:
 Remains 0 as it was never modified.
o Output:
 printf("%d %d\n", A, B); prints 2 0.
Final Output:
Combining all the outputs from the printf statements:

426220
Memory Organization:
 Global Variables:
o Stored in the data segment of the program's memory.
o Initialized data segment for variables explicitly initialized.
o BSS segment (Block Started by Symbol) for variables initialized to zero.
 Static Local Variables:
o Also stored in the data segment.
o Have function scope but program lifetime.
 Local Variables:
o Stored on the stack.
o Exist only during the execution of the function.

EX – 3
FIND OUTPUT
#include <stdio.h>
int count = 7; // Global variable
void F() {
static int X = 6; // Static local variable
X++; // Increment X
count++; // Increment count
printf("%d %d ", X, count);
}
int main() {
while (count <= 10) {
F();
}
return 0;
}

Final Output
Combining the outputs from each iteration:

7 8 8 9 9 10 10 11

EX – 4
FIND OUTPUT
#include <stdio.h>
void F(); // Function prototype
int count = 5; // Global variable

void F() {
static int I = 0; // Static local variable
++I; // Pre-increment I
printf("%d %d ", I, count);
}
int main() {
while (count--) { // Post-decrement count
F();
}
return 0;
}

Output with count--:


1423324150

Output with --count


14233241
 With count--:
o The loop includes the iteration where count is decremented from 1 to 0.
o We get an extra iteration, resulting in 5 total iterations.
 With --count:
o The loop exits before count reaches 0.
o We have only 4 iterations.

Register Storage Class in C


The register storage class is one of the storage classes in C, alongside auto, static, and
extern. It is used to suggest to the compiler that a variable should be stored in a CPU
register instead of RAM, potentially allowing for faster access.

Syntax
register data_type variable_name;
Example: register int i;
Key Points
 Purpose of register Keyword:
o The register keyword requests the compiler to store the variable in a CPU
register instead of RAM.
o It is a suggestion, not a command. The compiler may ignore this request if
there are no free registers available.
 Compiler's Role:
o The compiler decides whether to store the variable in a register based on
register availability.
o If no registers are available, the variable will be stored in RAM.
 Advantages of Register Variables:
o Faster Access: Accessing data from CPU registers is faster than accessing data
from RAM.
o Useful for variables that are frequently accessed, such as loop counters.
 Characteristics of Register Variables:
o Default Value: Undefined (contains a garbage value). Must be initialized
before use.
o Scope: Function scope (same as auto variables).
o Lifetime: Function lifetime (exists only during the function execution).
 Limitations:
o No Address Retrieval:
 You cannot use the address-of operator (&) on a register variable.
 This means you cannot obtain a pointer to a register variable.
o Pointer Incompatibility:
 Since you cannot get the address, you cannot use pointers with
register variables.
Examples and Explanations
 Incorrect Usage (Compiler Error):
void main() {
register int i;
scanf("%d", &i); // Error: Cannot take the address of a register variable
}
 Explanation: Attempting to use &i to get the address of i will cause a compiler error
because i is a register variable.

 Correct Usage:
void main() {
register int i = 0; // Directly initialize the variable
for (i = 0; i <= 10000; i++) {
// Loop code
}
}
 Explanation: By initializing the register variable directly and using it within a loop,
you can potentially improve the performance due to faster access.

When to Use Register Variables


 Ideal Candidates:
o Loop counters.
o Frequently accessed variables within performance-critical sections of code.
 Considerations:
o Due to the limited number of CPU registers, overusing the register keyword
may not yield benefits.
o Modern compilers are proficient at optimization, often making the register
keyword unnecessary.

Ex – 1
#include <stdio.h>
void prtfun();
int a = 1, b = 0, c = 0; // Global variables initialized
int main() {
static int t = 1; // Static local variable 't' initialized to 1
a++; // Increment global variable 'a' (a becomes 2)
prtfun(); // First call to 'prtfun'
a += 1; // Increment global variable 'a' by 1 (a becomes 3)
prtfun(); // Second call to 'prtfun'
printf("%d %d\n", a, b); // Print global variables 'a' and 'b' (outputs: 3 0)
return 0;
}
void prtfun() {
register int a = 2; // Register variable 'a' local to 'prtfun'
int b = 1; // Auto variable 'b' local to 'prtfun'
++b; // Increment 'b' (b becomes 2)
a = a + b; // Update 'a' (a becomes 4)
printf("%d %d\n", a, b); // Print local variables 'a' and 'b' (outputs: 4 2)
}

Program Output
42
42
30

Explanation of the Output


 First printf in prtfun():
o Outputs 4 2 (local a and b in the first call).
 Second printf in prtfun():
o Outputs 4 2 (local a and b in the second call).
 printf in main():
o Outputs 3 0 (global a and b).

GATE 2023 C Programming Question Involving Storage Classes


Program Code
#include <stdio.h>
int fncp();
int main() {
int x, y; // Auto (local) variables declared in 'main', initially with garbage values
x = fncp(); // First call to 'fncp', result stored in 'x'
y = fncp() + x; // Second call to 'fncp', result added to 'x' and stored in 'y'
printf("%d", x + y); // Print the sum of 'x' and 'y'
return 0;
}
int fncp() {
static int x = 1; // Static local variable 'x', initialized only once
x++; // Increment static variable 'x'
return x; // Return the current value of 'x'
}

Program Output

7
Why is the Output 7?
 The static variable x in fncp() retains its value between calls.
 The first call to fncp() increments x from 1 to 2 and returns 2.
 The second call increments x from 2 to 3 and returns 3.
 In main(), after the first call, x is 2.
 After the second call, y is 3 (from fncp) + 2 (current value of x in main) = 5.

 The sum x + y is 2 + 5 = 7.

EX – 2
Determine the return value of F2(5).
Function Definitions:
1. Function F1(N):

2. Function F2(N):

Step-by-Step Execution
1. Call F2(5)
 Since 5 > 0:
o Compute F1(5)
 F1(5) Execution:
 Calls itself with decreasing N until N = 0.
 Increments F1.i 5 times (for N = 5 to 1).
 F1.i after F1(5): 5
 Return Value: 5
o Update F2.i:
 F2.i = F2.i + F1(5) = 0 + 5 = 5
o Call F2(4)
2. Call F2(4)
 Since 4 > 0:
o Compute F1(4)
 F1(4) Execution:
 Calls itself with decreasing N until N = 0.
 Increments F1.i 4 times (for N = 4 to 1).
 F1.i after F1(4): 9 (previous 5 + 4 increments)
 Return Value: 9
o Update F2.i:
 F2.i = F2.i + F1(4) = 5 + 9 = 14
o Call F2(3)

3. Call F2(3)
 Since 3 > 0:
o Compute F1(3)
 F1(3) Execution:
 Calls itself with decreasing N until N = 0.
 Increments F1.i 3 times (for N = 3 to 1).
 F1.i after F1(3): 12 (previous 9 + 3 increments)
 Return Value: 12
o Update F2.i:
 F2.i = F2.i + F1(3) = 14 + 12 = 26
o Call F2(2)

4. Call F2(2)
 Since 2 > 0:
o Compute F1(2)
 F1(2) Execution:
 Calls itself with decreasing N until N = 0.
 Increments F1.i 2 times (for N = 2 to 1).
 F1.i after F1(2): 14 (previous 12 + 2 increments)
 Return Value: 14
o Update F2.i:
 F2.i = F2.i + F1(2) = 26 + 14 = 40
o Call F2(1)

5. Call F2(1)
 Since 1 > 0:
o Compute F1(1)
 F1(1) Execution:
 Calls itself with decreasing N until N = 0.
 Increments F1.i 1 time (for N = 1).
 F1.i after F1(1): 15 (previous 14 + 1 increment)
 Return Value: 15
o Update F2.i:
 F2.i = F2.i + F1(1) = 40 + 15 = 55
o Call F2(0)

6. Call F2(0)
 Since 0 > 0 is false:
o Recursion ends.
o Return F2.i = 55

Final Result

 F2(5) returns 55.

EX – 3
Program Code:
#include <stdio.h>
int total(int V) {
static int count = 0;
while (V) {
count += V & 1;
V = V >> 1;
}
return count;
}
int main() {
static int X = 0;
int I = 5;
while (I > 0) {
X = X + total(I);
I--;
}
printf("%d", X);
return 0;
}

Logic:
 While V is not zero:
o count += V & 1; checks if the least significant bit of V is 1 (i.e., if V is odd). If
so, increments count.
o V = V >> 1; shifts V one bit to the right, effectively dividing V by 2.

o EXAMPLE -
0111
& 0001
-------
0001 (Result is 1)

Step-by-Step Execution:
First Iteration (I = 5):
Call total(5)
 V=5
 count is static; initially count = 0
While Loop in total:
1. First Inner Loop Iteration:
o V = 5 (0b101)
o V & 1 = 1 (since 5 is odd)
o count = 0 + 1 = 1
o V = V >> 1 = 2 (0b10)
2. Second Inner Loop Iteration:
o V=2
o V & 1 = 0 (since 2 is even)
o count = 1 + 0 = 1
o V = V >> 1 = 1 (0b1)
3. Third Inner Loop Iteration:
o V=1
o V & 1 = 1 (since 1 is odd)
o count = 1 + 1 = 2
o V = V >> 1 = 0
Loop Ends (since V = 0)
 total(5) returns count = 2
Back to main:
 X = X + total(5) = 0 + 2 = 2
 Decrement I: I = 5 - 1 = 4

 …………………………………………………….SIMILARLY
APPLY FOR OTHER
ITERATIONS…………………………………………………………………

……………………………………………………………………………………………

End of Loop:
 I = 0, so the while loop in main ends.
Print X:

 The program prints X = 23.

Key Points:
 Bitwise Operations:
 V & 1 checks if V is odd (1) or even (0).
 V >> 1 effectively divides V by 2, shifting bits to the right.

 Function total: Counts the number of 1 bits in the binary representation of V,


cumulatively.
EX 3 AND 4
Version 1: Using post-increment operator with s initialized to 1

Version 2: Using pre-increment operator with s defaulting to 0 (since it's uninitialized).


Assignment with post-increment (a = s++)
 a gets the current value of s.
 Then, s is incremented by 1.

Step-by-Step Execution
Initial Call from main()
1. First Call to abc()
o s is initialized to 1.
o a = s++: a gets 1, then s becomes 2.
o Prints: 1 2
o Condition (a <= 2) is true (1 <= 2), so abc() is called recursively.
2. Second Call to abc()
o a = s++: a gets 2, then s becomes 3.
o Prints: 2 3
o Condition (a <= 2) is true (2 <= 2), so abc() is called recursively.
3. Third Call to abc()
o a = s++: a gets 3, then s becomes 4.
o Prints: 3 4
o Condition (a <= 2) is false (3 <= 2 is false), so no further recursion.
o Prints: 3 4 (second print in this call)
o Returns to the second call.
4. Back to Second Call
o Prints: 2 4 (second print in this call)
o Returns to the first call.
5. Back to First Call
o Prints: 1 4 (second print in this call)
o Returns to main().

Second Call from main()


6. Fourth Call to abc()
o a = s++: a gets 4, then s becomes 5.
o Prints: 4 5
o Condition (a <= 2) is false (4 <= 2 is false), so no recursion.
o Prints: 4 5 (second print in this call)
o Returns to main().

Total Number of Times printf() is Executed


 First Call Sequence: 6 times
o Prints in first call (before recursion): 1 2
o Prints in second call (before recursion): 2 3
o Prints in third call (before recursion): 3 4
o Prints in third call (after recursion): 3 4
o Prints in second call (after recursion): 2 4
o Prints in first call (after recursion): 1 4
 Second Call from main(): 2 times
o Prints in fourth call (before recursion): 4 5
o Prints in fourth call (after recursion): 4 5
Total printf() executions: 6 + 2 = 8 times
Final Output:
12
23
34
34
24
14
45
45

Explanation for Version 2


If we consider Version 2, where s is uninitialized and uses a pre-increment operator, the
execution would be similar, with the initial value of s starting from 0.
Key Differences:
 s starts from 0, so the first increment makes it 1.
 a = ++s: s increments first, then a gets the new value of s.

Output for Version 2:


11
22
33
33
23
13
44

44 Total printf() executions remain the same: 8 times.

EX – 6
We are asked to determine which of the following statements is true about this program:
1. The function returns zero for all values of I.
2. The function prints S for all values of I.
3. The function returns zero when I equal 50.
4. The function will exhaust the runtime stack or run into an infinite loop when I equals
50.
Evaluation of Options:
1. The function returns zero for all values of I.
o False. It does not return when I = 50 due to infinite recursion.
2. The function prints S for all values of I.
o False. It only prints S when I = 50.
3. The function returns zero when I equals 50.
o False. It does not return at all when I = 50 because of infinite recursion.
4. The function will exhaust the runtime stack or run into an infinite loop when I
equals 50.
o True. Infinite recursion leads to a stack overflow.
Conclusion:
 Correct Answer: Option 4.
 Explanation: When I equals 50, the function enters infinite recursion without a base
case to terminate, causing a stack overflow. For all other values of I, the function
returns 0 without printing or further recursion.

Recursive function
EX - 1

A recursive function is a function that calls itself during its execution. It typically consists of
two parts:
1. Base Case (Termination Condition): The condition under which the recursion ends.
2. Recursive Case (Inductive Step): The part where the function calls itself with
modified parameters.
Example Function:

This function calculates the factorial of N, denoted as N!.

Key Questions and Answers:


1. What is the return value of F(5)?
o Answer: 120
o Explanation

Calculating upwards:
2. What is the recurrence relation for the number of function calls T(N)?
 Answer:
Objective: Find a formula that represents the total number of times the function F is
called when computing F(N).

Explanation:
 The 1 accounts for the current function call.
 T(N - 1) accounts for the function calls made by the recursive call F(N - 1).

3. How many function calls are made for input N?


 Answer: N
 Explanation:
o Solving the recurrence relation:

This unfolds to:

So, T(N) = N.
4. What is the depth of the function calls (recursion depth) for input N?
 Answer: N
 Explanation:
o The recursion goes from N down to 1, creating N nested function calls on the
call stack.
o This depth affects the space complexity, which is O(N)
5. What is the recurrence relation for the return value F(N)?

 Answer

Explanation:
 This follows directly from the function definition.
 It represents the mathematical definition of factorial.

Additional Notes:
Space Complexity:
 Proportional to the recursion depth.
 For this function, space complexity is O(N) due to N activation records on the stack.
Time Complexity:
 Proportional to the number of function calls.
 Time complexity is also O(N).
 The relation T(N)=1+T(N−1) captures the idea that each call to F(N) results in one
additional function call, plus all the calls made to compute F(N - 1).
 Definition of T(N)): The total number of function calls made when computing F(N).
 Expanding T(N)):
Let's expand the recurrence relation step by step to find a pattern.
Conclusion:
 Total Function Calls: T(N)=NT(N) = NT(N)=N
Understanding the Result:
 Intuitive Explanation:
o For each value of NNN from the initial input down to 1, there's a
corresponding function call.
o The recursion unwinds after reaching the base case at N=1N = 1N=1.

 The recurrence relation for the return value F(N)F(N)F(N) effectively computes N!N!N!
(factorial of NNN).

Summary:
 Return Value of F(5): 120
 Number of Function Calls for Input N: N
 Recursion Depth for Input N: N

EX – 2
Function Definition:
We have a recursive function F(N) defined as follows:
 Base Case:
o If N = 0 or N = 1, then F(N) = 1.
 Recursive Case:
o If N > 1, then F(N) = F(N - 1) + F(N - 1).
In pseudocode:

Based on this function, we will address the following five questions:


1. What is the return value of F(5)?
2. What is the recurrence equation for the total number of function calls?
3. How many total function calls are made, calculated manually, when computing
F(5)?
4. What is the depth of the function calls (maximum recursion depth)?
5. What is the recurrence equation for the return value of the program, and solve it?

1. Return Value of F(5)


We are to compute F(5).
Given the recursive definition:
 F(N) = F(N - 1) + F(N - 1) for N > 1
 F(1) = 1
 F(0) = 1
Let's compute F(5) step by step.

Step-by-Step Computation:
1. F(5) = F(4) + F(4)
2. F(4) = F(3) + F(3)
3. F(3) = F(2) + F(2)
4. F(2) = F(1) + F(1)
5. F(1) = 1 (base case)
Now, compute upwards:
 F(2) = F(1) + F(1) = 1 + 1 = 2
 F(3) = F(2) + F(2) = 2 + 2 = 4
 F(4) = F(3) + F(3) = 4 + 4 = 8
 F(5) = F(4) + F(4) = 8 + 8 = 16
Answer: The return value of F(5) is 16.

2. Recurrence Equation for the Total Number of Function Calls


Let T(N) represent the total number of function calls made when computing F(N).
Recurrence Relation:
1. Base Cases:
For N = 0 or N = 1, only one function call is made:
T(0) = 1
T(1) = 1
2. Recursive Case (for N > 1):
Each call to F(N) results in:
 One call to F(N) itself (the current call).
 Two recursive calls to F(N - 1).
Therefore, the recurrence relation is:
T(N) = 1 + 2 * T(N - 1)
Derivation of the Closed-Form Solution:
We will solve this recurrence relation to find a closed-form expression for T(N).

Step-by-Step Solution:
1. First Expansion:
T(N) = 1 + 2 * T(N - 1)
2. Second Expansion:
Expand T(N - 1):
T(N - 1) = 1 + 2 * T(N - 2)
Substitute back into T(N):
T(N) = 1 + 2 * (1 + 2 * T(N - 2))
= 1 + 2 * 1 + 2 * 2 * T(N - 2)
= 1 + 2 + 4 * T(N - 2)

3. Third Expansion:

Expand T(N - 2):


T(N - 2) = 1 + 2 * T(N - 3)

Substitute back:
T(N) = 1 + 2 + 4 * (1 + 2 * T(N - 3))
= 1 + 2 + 4 * 1 + 4 * 2 * T(N - 3)
= 1 + 2 + 4 + 8 * T(N - 3)

4. Continuing the Pattern:


After k expansions, the expression becomes:

T(N) = 1 + 2 + 4 + 8 + ... + 2^(k-2) + 2^(k-1) * T(N - (k - 1))

Since N - (k - 1) = 1, this simplifies to N = k.


Therefore, after N expansions, we get:
T(N) = 1 + 2 + 4 + 8 + ... + 2^(N-1) * T(1)

Since T(1) = 1 (from the base case), we have:


T(N) = 1 + 2 + 4 + 8 + ... + 2^(N-1)

5. Sum of the Geometric Series:


The sum of this geometric series is:
T(N) = 2^N - 1

6. Final Closed-Form Solution:


T(N) = 2^N - 1
3. Total Number of Function Calls for F(5)
Using the formula from question 2:
T(5) = 2^{5} - 1 = 32 - 1 = 31
So, 31 function calls are made when computing F(5).
Manual Verification:
Let's confirm this by manually counting the function calls.
Counting all function calls:
 Level N=5: 1 call (F(5))
 Level N=4: 2 calls (F(4) called twice)
 Level N=3: 4 calls (F(3) called four times)
 Level N=2: 8 calls (F(2) called eight times)
 Level N=1: 16 calls (F(1) called sixteen times)
Total function calls:
1+2+4+8+16 = 31

This confirms that 31 function calls are made when computing F(5).

4. Depth of the Function Calls (Maximum Recursion Depth)


The depth of the recursion tree corresponds to the maximum number of nested function
calls active at any point during execution.
In the recursive function F(N), each call reduces N by 1 until it reaches the base case N = 0
or N = 1.
Therefore, the maximum depth D(N) is:
D(N)=N
For N = 5:
D(5)=5
Answer: The maximum depth of the function calls when computing F(5) is 5.

5. Recurrence Equation for the Return Value of the Program


We need to find a recurrence relation for the return value F(N) and solve it.
Recurrence Relation:
 Base Case:
o For N = 0 or N = 1: F(N)=1
 Recursive Case (for N > 1):
o The function is defined as: F(N)=F(N−1)+F(N−1
o Simplifying: F(N)=2×F(N−1)Solving the Recurrence Relation:
We can solve this recurrence relation using repeated substitution.
Method:

Answer: The return value F(N) is:


F(N)=2^N−1
Verification:
Let's verify this formula with N = 5:

F(5) = 2^{5 - 1} = 2^{4} = 16


This matches the value computed in question 1.

Summary
 Return Value of F(N):
F(N) = 2^(N-1)
o For N = 5: F(5)=16
 Total Number of Function Calls:
T(N) = (2^N) – 1
o For N = 5: T(5) = 31

 Maximum Depth of Function Calls:


D(N) = N
o For N = 5: D(5) = 5

EX – 3
Given Recursive Function:
Our Tasks:
1. Compute the return value of do(16384).
2. Derive the recurrence equation for the total number of function calls.
3. Determine the depth of the function calls.

1)
Compute do(16384)
o Since 16384 > 2, we proceed to the else part.
o Compute floor(sqrt(16384)) = floor(128) = 128.
o So, do(16384) = do(128) + 16384.
Compute do(128)
o Since 128 > 2, we proceed to the else part.
o Compute floor(sqrt(128)).
 sqrt(128) ≈ 11.3137.
 floor(11.3137) = 11.
o So, do(128) = do(11) + 128.
Compute do(11)
o Since 11 > 2, we proceed to the else part.
o Compute floor(sqrt(11)).
 sqrt(11) ≈ 3.3166.
 floor(3.3166) = 3.
o So, do(11) = do(3) + 11.
Compute do(3)
o Since 3 > 2, we proceed to the else part.
o Compute floor(sqrt(3)).
 sqrt(3) ≈ 1.732.
 floor(1.732) = 1.
o So, do(3) = do(1) + 3.
Compute do(1)
o Since 1 <= 2, we hit the base case.
o So, do(1) = 1.
Backtracking and Summing Up:
 do(1) = 1
 do(3) = do(1) + 3 = 1 + 3 = 4
 do(11) = do(3) + 11 = 4 + 11 = 15
 do(128) = do(11) + 128 = 15 + 128 = 143
 do(16384) = do(128) + 16384 = 143 + 16384 = 16527
Answer:

The return value of do(16384) is 16527.

2)
Step 1: Define the Total Number of Function Calls
Let T(N) represent the total number of function calls made when do(N) is executed.

Step 2: Establish the Recurrence Relation


 Base Case (when N <= 2):
o The function returns immediately without any further recursive calls.
o So, T(N) = 1.
 Recursive Case (when N > 2):
o The function makes one recursive call to do(floor(sqrt(N))).
o It also counts as one function call for the current invocation.
o So, T(N) = 1 + T(floor(sqrt(N))).
Recurrence Relation:
Step 3: Solve the Recurrence Relation
We will solve this recurrence relation to find T(N) in terms of N.
First, understand how T(N) expands:
1. First Call:

2. Second Call:

3. Third Call:

4. **And so on, until we reach the base case where N <= 2.

Each time, we add 1 to account for the current function call.

Step 4: Generalize the Recurrence


Let's introduce an iteration counter k to represent the number of times we apply the square
root.
At the k-th iteration, the input to the function is:

This is because each square root operation raises N to the power of 1/2. Applying it k-1
times results in the exponent (1/2)^{k-1}.

Total Function Calls up to the k-th Iteration:


 (k - 1) accounts for the number of function calls made so far.
 T(N_k) is the total number of function calls starting from N_k.

Step 5: Determine When to Stop the Recursion


We stop the recursion when we reach the base case, i.e., when N_k <= 2.
So, we need to find the value of k such that:

Step 6.1: Set Up the Equation


We have:

Step 6.2: Take the Logarithm of Both Sides


Logarithms help us solve equations where the unknown is an exponent.
We will use logarithm base 2 (log₂) for simplicity.
First, understand what log₂ means:
 log₂(N) is the exponent to which 2 must be raised to get N.
 For example, log₂(8) = 3 because 2^3 = 8.
Now, take log₂ of both sides of the equation:

Step 6.3: Simplify Using Logarithm Properties


Property Used:

So, the left side becomes:


The right side is:

Our Equation Now:

Step 6.4: Solve for (1/2)^{k-1}


Rewriting the equation:

Step 6.5: Solve for k


Now, take the logarithm base 2 of both sides to solve for k.
First, understand another logarithm property:

But before that, note that:

So:

Now, take log₂ of both sides:

Simplify both sides:


 Left Side:

 Right Side:

So the equation becomes:

Step 6.6: Simplify and Solve for k

Multiply both sides by -1:

Finally, solve for k:

Step 7: Determine Total Number of Function Calls T(N)


Recall that:
 The total number of function calls is:

 Since N_k <= 2, and from the base case, T(N_k) = 1.


So:

Substitute the value of k we found:


3)
The depth of the function calls is the maximum number of function calls on the call stack at
any point during execution. Since each call to do(N) results in a single recursive call to
do(floor(sqrt(N))), the depth is equal to the total number of function calls.
Answer:
The depth of the function calls is:

Additional Notes:
Understanding Logarithms in This Context

 Why Use Logarithms?


o Logarithms help solve equations where the unknown variable is an exponent.
o In our case, the exponent (1/2)^{k-1} is in the power of N, and we need to
solve for k.
 Key Logarithm Concepts:
o Logarithm Definition: log_b(a) = c means b^c = a.
o Logarithm of a Power: log_b(a^c) = c * log_b(a).
o Logarithm of 1 Over a Number: log_b(1/a) = -log_b(a).

Understanding the Return Value Recurrence Relation:


 The recurrence for the return value R(N) is:
Base Case (N ≤ 2):

Recursive Case (N > 2):


EX – 4

In this function:
 If N is greater than zero, we return N % R plus a recursive call to F(N / R, R).
 If N is less than or equal to zero, we return 0.
You can expect questions like this:
 What is the return value of F(345, 10)?
 What is the return value of F(513, 2)?
I will explain how to compute the return value of F(345, 10). You should try computing the
return value of F(513, 2) by yourself.

Computing the Return Value of F(345, 10)


1. First Call: F(345, 10)
o N = 345, R = 10
o Since N > 0, compute:
 N % R = 345 % 10 = 5
 Recursive call: F(345 / 10, 10) = F(34, 10)
o Return: 5 + F(34, 10)
2. Second Call: F(34, 10)
o N = 34, R = 10
o Since N > 0, compute:
 N % R = 34 % 10 = 4
 Recursive call: F(34 / 10, 10) = F(3, 10)
o Return: 4 + F(3, 10)
3. Third Call: F(3, 10)
o N = 3, R = 10
o Since N > 0, compute:
 N % R = 3 % 10 = 3
 Recursive call: F(3 / 10, 10) = F(0, 10)
o Return: 3 + F(0, 10)
4. Fourth Call: F(0, 10)
o N = 0, R = 10
o Since N <= 0, return 0.
Now, we can compute the return values back up the call stack:
 F(0, 10) returns 0.
 F(3, 10) returns 3 + 0 = 3.
 F(34, 10) returns 4 + 3 = 7.
 F(345, 10) returns 5 + 7 = 12.
The return value of F(345, 10) is 12.
Recurrence Equation for Total Number of Function Calls
Let's derive the recurrence equation for the total number of function calls in this program.
Definition:
 Let T(N) denote the total number of function calls for F(N, R).
Recurrence Relation:
 If N > 0:
o T(N) = 1 + T(N / R)
 The 1 accounts for the current function call.
 T(N / R) represents the number of function calls for the recursive call
with N / R.
 If N <= 0:
o T(N) = 1
 Only the current function call is made, and recursion stops.
Solving the Recurrence:
1. Expand the Relation:

2. Generalize the Expansion:


 After k recursive calls:

3. Determine the Stopping Condition:

 Recurrence Relation:
When
The function makes a recursive call with N/RN / RN/R. Therefore:

The 1 accounts for the current function call.


When
The function returns 0 without further recursion, but we still count the
current call:

 Expanding the Recurrence:


Let's expand T(N)T(N)T(N) step by step.
 First Call:

T(n) -> 1st call


T(n/r)-> 2nd call
 Second Call: T(n/r^2)-> 3rd
So,

 Third Call:

So,

 Continuing in this manner, after kth step:

 Determining the Stopping Condition:


 We need to find the value of kkk where the recursion
terminates. The recursion stops when
 Set the base case:

However, since are positive integers,


will be positive until becomes less than 1.

 Set the stopping condition:

Solving for k:

Taking logarithms:

Since k−1 must be an integer, we have:


 Calculating T(N):

 Total number of function calls:

 Substitute k:

Final Result:
The total number of function calls made by F(N, R) is:
Explanation:
 + 1 calculates how many times N can be divided by R before it becomes
less than 1.
 The +1 accounts for the base case when N ≤ 0.
 In this case Depth of function call same as total number of function calls

SIMPLER APPROACH
INSTEAD OF DEALING WITH INEQUALITIES

NOW JUST DIRECTLY SUBSTITUTE K – 1 VALUE INTO


EX – 5
Function F(N):
int X = 1, K;
If N == 1:
Return X
Else:
For (K = 1; K < N; ++K)
X = X + F(K) * F(N - K)
Return X

Objective:
Compute the return value of F(5) using the corrected function.

Step-by-Step Computation:
Base Case (N = 1):
 Since N == 1, the function returns X, which is initialized to 1.
 Therefore, F(1) = 1.

Calculating F(2):
 Initialize: X = 1
 Loop: K from 1 to N - 1 (since K < N, K goes from 1 to 1)
Iteration (K = 1):
 Compute X = X + F(1) * F(1)
 We know F(1) = 1
 So, X = 1 + 1 * 1 = 2
 Return: X = 2
 Therefore, F(2) = 2
Calculating F(3):
 Initialize: X = 1
 Loop: K from 1 to 2
Iteration (K = 1):
 Compute X = X + F(1) * F(2)
 We know F(1) = 1, F(2) = 2
 So, X = 1 + 1 * 2 = 3
Iteration (K = 2):
 Compute X = X + F(2) * F(1)
 We know F(2) = 2, F(1) = 1
 So, X = 3 + 2 * 1 = 5
 Return: X = 5
 Therefore, F(3) = 5

Calculating F(4):
 Initialize: X = 1
 Loop: K from 1 to 3
Iteration (K = 1):
 Compute X = X + F(1) * F(3)
 We know F(1) = 1, F(3) = 5
 So, X = 1 + 1 * 5 = 6
Iteration (K = 2):
 Compute X = X + F(2) * F(2)
 We know F(2) = 2
 So, X = 6 + 2 * 2 = 10
Iteration (K = 3):
 Compute X = X + F(3) * F(1)
 We know F(3) = 5, F(1) = 1
 So, X = 10 + 5 * 1 = 15
 Return: X = 15
 Therefore, F(4) = 15

Calculating F(5):
 Initialize: X = 1
 Loop: K from 1 to 4
Iteration (K = 1):
 Compute X = X + F(1) * F(4)
 We know F(1) = 1, F(4) = 15
 So, X = 1 + 1 * 15 = 16
Iteration (K = 2):
 Compute X = X + F(2) * F(3)
 We know F(2) = 2, F(3) = 5
 So, X = 16 + 2 * 5 = 26
Iteration (K = 3):
 Compute X = X + F(3) * F(2)
 We know F(3) = 5, F(2) = 2
 So, X = 26 + 5 * 2 = 36
Iteration (K = 4):
 Compute X = X + F(4) * F(1)
 We know F(4) = 15, F(1) = 1
 So, X = 36 + 15 * 1 = 51
 Return: X = 51
 Therefore, F(5) = 51
Summary of Computed Values:
 F(1) = 1
 F(2) = 2
 F(3) = 5
 F(4) = 15
 F(5) = 51

Final Answer:
The return value of F(5) is 51.
EX – 6

Program 1: Abnormal Termination Due to Stack Overflow


Code:
int F(int V) {
int X = 0;
while (V) {
X = X + F(V--);
}
return V;
}
// Calling the function
int result = F(3);

Explanation:
1. Initial Call: We start by calling F(3). Inside the function:
o V is initialized to 3.
o X is initialized to 0.
2. While Loop Condition: The condition while (V) checks if V is non-zero. Since V = 3,
the condition is true, and we enter the loop.
3. Inside the Loop:
o The statement X = X + F(V--) is executed.
o The V-- is a post-decrement operator. This means the current value of V is
used in the function call, and then V is decremented.
o Function Call: F(V--) translates to F(3), and then V becomes 2.
4. Recursive Calls:
o The function F(3) is called recursively.
o Each recursive call follows the same steps:
 V is initialized to 3.
 X is initialized to 0.
 The while (V) condition is true since V = 3.
 Inside the loop, X = X + F(V--) calls F(3) again before decrementing V.
5. Infinite Recursion:
o The value of V inside each recursive call remains 3 at the point of the
function call due to the post-decrement.
o This leads to infinite recursion with each call stacking up new activation
records (function call instances) on the call stack.
6. Stack Overflow and Abnormal Termination:
o Since each recursive call consumes stack space and there's no base case
being reached, the stack keeps growing.
o Eventually, the program runs out of stack memory, causing a stack
overflow.
o The program terminates abnormally due to this unhandled stack overflow.

Conclusion:
 Return Value: The function does not return a value because it terminates
abnormally.
 Behavior: Abnormal termination due to stack overflow.

Program 2: Infinite Loop Without Stack Overflow


Code:
int F(int V) {
int X = 0;
while (V) {
X = X + F(V - 1);
}
return V;
}
// Calling the function
int result = F(3);
Explanation:
1. Initial Call: We start by calling F(3). Inside the function:
o V is initialized to 3.
o X is initialized to 0.
2. While Loop Condition: The condition while (V) checks if V is non-zero. Since V = 3,
the condition is true, and we enter the loop.
3. Inside the Loop:
o The statement X = X + F(V - 1) is executed.
o Function Call: F(V - 1) translates to F(2).
o Note that V is not modified after the function call. It remains 3.
4. Recursive Calls:
o The function F(2) is called.
 Inside F(2):
 V is 2, X is 0.
 while (V) is true.
 X = X + F(1).
o The function F(1) is called.
 Inside F(1):
 V is 1, X is 0.
 while (V) is true.
 X = X + F(0).
o The function F(0) is called.
 Inside F(0):
 V is 0, X is 0.
 while (V) is false, so we skip the loop.
 return V; returns 0.
5. Unwinding the Stack:
o F(0) returns 0 to F(1).
o In F(1):
 X = 0 + 0 = 0.
 The while (V) condition is still true (since V = 1 and hasn't changed).
 The loop repeats, calling F(0) again.
o This results in an infinite loop within F(1).
6. Stack Behavior:
o Each time F(0) is called, it returns immediately, and the activation record is
removed from the stack.
o The number of activation records oscillates between a small number (e.g., 3
or 4) and does not grow indefinitely.
o Since the stack does not overflow, the program does not terminate
abnormally.

Conclusion:
 Return Value: The function does not return a value because it enters an infinite
loop.
 Behavior: Infinite loop without causing stack overflow.

Modification of V:
 Program 1: Uses V-- (post-decrement). V is decremented after the function call, but
each recursive call uses the same initial value (V = 3), leading to infinite recursion.
 Program 2: Uses V - 1. V is not changed in the caller's context. The recursive calls
reach the base case (V = 0), but the loop in each function does not terminate
because V remains the same.

Visual Representation of Stack Behavior:


Program 1 Stack Growth (Abnormal Termination):
Program 2 Stack Oscillation (Infinite Loop):
EX – 7
We have a function get that accepts two integer arguments A and B. The function is
defined with the following conditions:

Problem Statement:
1. Calculate the return value of get(3, 2).
2. Determine how many function calls are made during the execution of get(3, 2).

Step-by-Step Execution and Analysis:


We will compute get(3, 2) and keep track of each function call.
1. Initial Call:
 Function Call: get(3, 2)
 Parameters: A = 3, B = 2
 Base Conditions:
o B == 0? No (B = 2)
o A < B? No (3 < 2 is false)
 Recursive Calls:
o get(3 - 1, 2) → get(2, 2)
o get(3 - 1, 2 - 1) → get(2, 1)
2. First Recursive Call: get(2, 2)
 Function Call: get(2, 2)
 Parameters: A = 2, B = 2
 Base Conditions:
o B == 0? No
o A < B? No (2 < 2 is false)
 Recursive Calls:
o get(2 - 1, 2) → get(1, 2)
o get(2 - 1, 2 - 1) → get(1, 1)
3. Second Recursive Call: get(1, 2)
 Function Call: get(1, 2)
 Parameters: A = 1, B = 2
 Base Conditions:
o B == 0? No
o A < B? Yes (1 < 2 is true)
 Return Value: 0
4. Third Recursive Call: get(1, 1)
 Function Call: get(1, 1)
 Parameters: A = 1, B = 1
 Base Conditions:
o B == 0? No
o A < B? No
 Recursive Calls:
o get(1 - 1, 1) → get(0, 1)
o get(1 - 1, 1 - 1) → get(0, 0)
5. Fourth Recursive Call: get(0, 1)
 Function Call: get(0, 1)
 Parameters: A = 0, B = 1
 Base Conditions:
o B == 0? No
o A < B? Yes (0 < 1 is true)
 Return Value: 0
6. Fifth Recursive Call: get(0, 0)
 Function Call: get(0, 0)
 Parameters: A = 0, B = 0
 Base Conditions:
o B == 0? Yes
 Return Value: 1
Back to get(1, 1):
 Compute Return Value: get(0, 1) + get(0, 0) → 0 + 1 → 1
 Return Value: 1
Back to get(2, 2):
 Compute Return Value: get(1, 2) + get(1, 1) → 0 + 1 → 1
 Return Value: 1
7. Sixth Recursive Call: get(2, 1)
 Function Call: get(2, 1)
 Parameters: A = 2, B = 1
 Base Conditions:
o B == 0? No
o A < B? No
 Recursive Calls:
o get(2 - 1, 1) → get(1, 1)
o get(2 - 1, 1 - 1) → get(1, 0)
8. Seventh Recursive Call: get(1, 1)
 Function Call: get(1, 1)
 Parameters: A = 1, B = 1
 This call was previously computed and returned 1.
9. Eighth Recursive Call: get(1, 0)
 Function Call: get(1, 0)
 Parameters: A = 1, B = 0
 Base Conditions:
o B == 0? Yes
 Return Value: 1
Back to get(2, 1):
 Compute Return Value: get(1, 1) + get(1, 0) → 1 + 1 → 2
 Return Value: 2
Back to get(3, 2):
 Compute Return Value: get(2, 2) + get(2, 1) → 1 + 2 → 3
 Return Value: 3

Final Results:
1. Return Value of get(3, 2): 3
2. Total Number of Function Calls:
Let's list all the function calls, including repeated calls:
o get(3, 2)
o get(2, 2)
o get(1, 2)
o get(1, 1)
o get(0, 1)
o get(0, 0)
o get(2, 1)
o get(1, 1) (already computed but called again)
o get(0, 1) (already computed but called again)
o get(0, 0) (already computed but called again)
o get(1, 0)

Total Function Calls: 11


Visualization with Recursion Tree:
Here's a recursion tree to visualize the function calls:
EX – 8
We have a program with a recursive function foo. The main function initializes two
variables and calls foo. We need to determine the output of the program.
Program Code:

Objective:
 Determine the output of the program when executed.
 Understand why certain variables retain or do not retain their values after function
calls.

Shortcut Approach:
1. Identify Key Variables:
o In main(), we have int a = 2048; and int sum = 0;.
2. Function Call:
o We call foo(a, sum);.
3. Understanding Variable Scope:
o The variable sum is passed by value to foo. Any changes to sum within foo
do not affect sum in main.
4. Printing sum in main:
o After the call to foo, we execute printf("%d", sum);.
o Since sum in main remains unchanged (still 0), the last printed value is 0.
5. Analyzing printf Statements:
o Within foo, printf("%d", k); is executed after the recursive call.
o This means the digits of n are printed in the order they are processed, after
the recursion unwinds.
6. Digits of n:
o The number 2048 has digits 2, 0, 4, 8.
o These digits are printed in reverse order of processing due to recursion.
7. Constructing the Output:
o The digits printed are: 2, 0, 4, 8, followed by 0 from sum in main.
o Therefore, the output is: 20480.
8. Eliminating Options:
o If given multiple-choice options, and knowing the last digit printed is 0, we
can eliminate any options where the last digit is not 0.
Conclusion:
 The output of the program is 2048

Detailed Approach:
 Base Case: If n == 0, the function returns without doing anything.
 Recursive Case:
o Calculate k as the last digit of n (n % 10).
o Calculate j as n without its last digit (n / 10).
o Update sum by adding k to it.
o Recursively call foo(j, sum).
o After the recursive call, print the digit k.
2. Variable Scope and Passing by Value:
 Variables in main:
o a and sum are local to main.
 Variables in foo:
o n and sum are parameters, passed by value.
o k and j are local variables in each call to foo.
 Important: Changes to sum inside foo do not affect the sum in main because it's
passed by value, not by reference.
3. Execution Flow:
Let's trace the function calls and variable values step by step.

First Call: foo(2048, 0)


 Variables:
o n = 2048
o sum = 0
o k and j will be calculated.
 Check Base Case: n != 0, proceed.
 Compute:
o k = 2048 % 10 = 8
o j = 2048 / 10 = 204
o sum = 0 + 8 = 8
 Recursive Call: foo(204, 8)
 After Recursion: printf("%d", k); prints 8.

Second Call: foo(204, 8)


 Variables:
o n = 204
o sum = 8
 Check Base Case: n != 0, proceed.
 Compute:
o k = 204 % 10 = 4
o j = 204 / 10 = 20
o sum = 8 + 4 = 12
 Recursive Call: foo(20, 12)
 After Recursion: printf("%d", k); prints 4.

Third Call: foo(20, 12)


 Variables:
o n = 20
o sum = 12
 Check Base Case: n != 0, proceed.
 Compute:
o k = 20 % 10 = 0
o j = 20 / 10 = 2
o sum = 12 + 0 = 12
 Recursive Call: foo(2, 12)
 After Recursion: printf("%d", k); prints 0.

Fourth Call: foo(2, 12)


 Variables:
o n=2
o sum = 12
 Check Base Case: n != 0, proceed.
 Compute:
o k = 2 % 10 = 2
o j = 2 / 10 = 0
o sum = 12 + 2 = 14
 Recursive Call: foo(0, 14)
 After Recursion: printf("%d", k); prints 2.
Fifth Call: foo(0, 14)
 Variables:
o n=0
o sum = 14
 Check Base Case: n == 0, function returns.
 No further action.

4. Unwinding the Recursion:


As the recursion unwinds, the printf("%d", k); statements execute in the following order:
1. Fourth Call: Prints 2
2. Third Call: Prints 0
3. Second Call: Prints 4
4. First Call: Prints 8
So, the digits printed are 2 0 4 8 in that order.
5. Printing sum in main:
 After all recursive calls are complete, main executes printf("%d", sum);.
 Value of sum in main: Still 0 (unchanged).
 Therefore, the last printed value is 0.

Final Output:
Putting it all together, the program outputs:
EX – 9
The Function jumble
Question:
In GATE 2019, the following question was asked:
We have a main function that calls a function jumble:

What is the output of this program?

Answer:
Step-by-Step Execution:
1. Initial Values in main:
o x=2
o y=5
2. First Call to jumble: y = jumble(y, x);
o Parameters Passed: x = y = 5, y = x = 2
o Inside jumble:
 Compute x = 2 * x + y
 Substitute values: x = 2 * 5 + 2 = 10 + 2 = 12
 Return Value: 12
o Update in main:
 y = 12
3. Second Call to jumble: x = jumble(y, x);
o Parameters Passed: x = y = 12, y = x = 2
o Inside jumble:
 Compute x = 2 * x + y
 Substitute values: x = 2 * 12 + 2 = 24 + 2 = 26
 Return Value: 26
o Update in main:
 x = 26
4. Print Statement: printf("%d", x);
o Output: 26

Explanation:
 First Call (jumble(5, 2)):
o The function calculates x = 2 * x + y.
o Substituting the values: x = 2 * 5 + 2 = 12.
o The function returns 12, which is assigned to y in main.
 Second Call (jumble(12, 2)):
o Now, y is 12 from the previous step, and x remains 2.
o Inside jumble, compute x = 2 * 12 + 2 = 26.
o The function returns 26, which is assigned to x in main.
 Final Output:
o The value of x in main is now 26.
o The printf statement outputs this value.

Conclusion:
 The output of the program is:
EX – 10
The Recursive Function convert
Question:
Consider the following recursive function:

Question: Which one of the following will happen when the function convert is called with
any positive integer N?
Options:
1. It will print the binary representation of N and terminate the program.
2. It will print the binary representation of N but will not terminate.
3. It will print the binary representation of N in reverse order and terminate the
program.
4. It will not print anything and will not terminate.

Answer:
Analyzing the Function:
Let's analyze the function step by step with a positive integer. We'll use N = 8 as an
example.
Function Definition:
Base Case:
 The base case checks if n < 0. Since n is a positive integer, this condition is false for
n >= 0.
Recursive Case:
 The function calls itself with n / 2 (integer division).
 After the recursive call, it prints n % 2.
Problematic Behavior:
 When n becomes 0, the function calls convert(0 / 2) which is convert(0).
 This creates a recursive call with n = 0.
 The condition n < 0 is still false for n = 0.
 The function continues to call itself infinitely with n = 0, leading to infinite
recursion.
Demonstration with N = 8:
1. First Call: convert(8)
o Calls convert(4) and waits to execute printf("%d", 8 % 2);
2. Second Call: convert(4)
o Calls convert(2) and waits to execute printf("%d", 4 % 2);
3. Third Call: convert(2)
o Calls convert(1) and waits to execute printf("%d", 2 % 2);
4. Fourth Call: convert(1)
o Calls convert(0) and waits to execute printf("%d", 1 % 2);
5. Fifth Call: convert(0)
o Calls convert(0) and waits to execute printf("%d", 0 % 2);
6. Infinite Recursion:
o The function keeps calling convert(0) indefinitely.
o The base case n < 0 is never met for n = 0.
Result:
 Infinite Recursion: The function enters an infinite loop of recursive calls with n = 0.
 Stack Overflow: Eventually, the program will crash due to stack overflow.
 No Output: Since the printf statements are after the recursive calls and the
recursion never ends, no output is printed before the crash.

Conclusion:

 Correct Option: Option 4.


Option 4: It will not print anything and will not terminate.
 The function will not print anything and will not terminate because it enters
infinite recursion due to the lack of a proper base case for n = 0.

Explanation:
 Why Doesn't It Terminate?
o The base case if (n < 0) is insufficient for positive integers.
o For n = 0, n < 0 is false, so the function does not return.
o It keeps calling convert(0) indefinitely.
 How to Fix the Function:
o Modify the base case to handle n <= 0.
EX – 11
Problem Statement:
In GATE 2015, the following question was asked:
Question:
If get(6) is called from the main function, then how many times will the get function be
invoked before returning to the main function?
Provided Program: (OG QUESTION)

Objective:
Determine the total number of times the get function is invoked when get(6) is called.

Understanding the Function:


 Base Case: If n < 1, the function returns immediately.
 Recursive Calls: If n >= 1, the function makes two recursive calls:
o get(n - 1)
o get(n - 2)
 Print Statement: After the recursive calls, the function prints the value of n.

Approach:
To find the total number of function calls when get(6) is invoked, we can:
1. Construct a recurrence relation that models the number of function calls.
2. Compute the number of function calls using the recurrence relation.

Constructing the Recurrence Relation:


Let’s define T(n) as the total number of times the get function is called when get(n) is
invoked.
 Base Case:
o For n < 1, the function returns immediately, but since the function was
called, we count it as 1 function call.
o Therefore, T(n) = 1 for n < 1.
 Recursive Case:
o For n >= 1, the function calls itself twice and counts as one function call.
o Therefore, the recurrence relation is:
T(n) = 1 + T(n - 1) + T(n - 2) for n >= 1

Calculations:
Now, we'll compute T(n) for n from 0 to 6.

Base Cases:
1. For n = 0:
o Since n < 1, T(0) = 1.
2. For n = -1:
o Since n < 1, T(-1) = 1.
Recursive Cases:
We will compute T(n) step by step.
Conclusion:
 Total Number of Function Calls when get(6) is invoked is 41.

Detailed Explanation:
Let's break down how these function calls are made when get(6) is invoked.

Recursive Call Tree:


We can visualize the function calls using a tree structure.
1. Level 0:
o get(6)
2. Level 1:
o get(6 - 1) → get(5)
o get(6 - 2) → get(4)
3. Level 2:
o From get(5):
 get(5 - 1) → get(4)
 get(5 - 2) → get(3)
o From get(4):
 get(4 - 1) → get(3)
 get(4 - 2) → get(2)
4. Level 3:
o From get(4) (from get(5)):
 get(4 - 1) → get(3)
 get(4 - 2) → get(2)
o From get(3):
 get(3 - 1) → get(2)
 get(3 - 2) → get(1)
o From get(3) (from get(4)):
 get(3 - 1) → get(2)
 get(3 - 2) → get(1)
o From get(2):
 get(2 - 1) → get(1)
 get(2 - 2) → get(0)
5. Level 4 and beyond:
o This pattern continues until all calls reach n < 1, where the base case
applies.

Counting Function Calls:


We can see that there are overlapping calls due to the nature of recursion in this function.
However, when counting function calls, we must count every call, including duplicates.
Here's how the counts add up:
 T(6) = 41 function calls
EX – 12
Recursive Function sum
Question:
In GATE 2021, a question was asked involving a recursive function named sum. The function
is defined as follows:

Question:
What is the return value when sum is called with the inputs x = 15 and y = 255? That is,
compute sum(15, 255).

Answer:
Understanding the Function:
The function sum recursively computes a value based on the inputs x and y using the
following logic:
1. Base Cases:
o If x == 0, return y.
o If y == 0, return x.
o If x == y, return x.
2. Recursive Cases:
o If x > y, return sum(x - y, y).
o Else (i.e., if x < y), return sum(x, y - x).
Given Inputs:
 x = 15
 y = 255
Step-by-Step Execution:
Let's evaluate sum(15, 255) step by step.
1. First Call: sum(15, 255)
o x = 15, y = 255
o None of the base cases are met:
 x != 0
 y != 0
 x != y
o Since x < y (15 < 255), we proceed to the else case:
 Return sum(15, 255 - 15) → sum(15, 240)
2. Second Call: sum(15, 240)
o x = 15, y = 240
o None of the base cases are met.
o Since x < y (15 < 240), we proceed to the else case:
 Return sum(15, 240 - 15) → sum(15, 225)
3. Third Call: sum(15, 225)
o x = 15, y = 225
o None of the base cases are met.
o Since x < y, return sum(15, 225 - 15) → sum(15, 210)
4. Continue this pattern:
We notice that in each recursive call, y is reduced by x (which is 15). We can represent the
sequence of calls as:

1. At sum(15, 15):
o Now, x = 15 and y = 15.
o The base case x == y is met.
o Return x (or y), which is 15.
2. Unwinding the Recursion:
o Each recursive call returns the value 15 back to the previous call.
o Since there are no additional computations after the recursive calls, the
final result is 15.
Conclusion:
 The function sum(15, 255) returns 15.
EX – 13
Activation Tree and Function Calls
Question:
In GATE 2023, a question was asked about interpreting an activation tree corresponding to a
main function in a program. The program includes functions main, F1, F2, and F3, with the
following definitions:
// Function definitions
int F1() {
return 1;
}
int F2(int x) {
F3();
if (x == 1)
return F1();
else
return F2(x - 1);
}

int F3() {
return 5;
}
int main() {
F1();
F2(2);
F3();
return 0;
}
Question:
Which of the following options represents the correct activation tree corresponding to the
main function's execution?
(Options are provided as diagrams illustrating the activation tree.)

Answer:
Understanding the Activation Tree:
An activation tree represents the hierarchy and order of function calls during the execution
of a program. Each node represents a function activation (call), and edges represent the calls
from one function to another.
Given Function Calls in main:
1. F1();
2. F2(2);
3. F3();
We need to determine the order and nesting of function calls based on the program's logic.
Step-by-Step Execution:
1. main Function Starts:
o The operating system calls main.
o main begins execution.
2. First Call in main: F1();
o main calls F1.
o F1 executes and returns 1.
o Control returns to main.
3. Second Call in main: F2(2);
o main calls F2(2).
o Within F2(2):
 Call to F3();
 F2 calls F3.
 F3 executes and returns 5.
 Control returns to F2(2).
 Conditional Check: if (x == 1)
 x = 2, so x == 1 is false.
 Else Case:
 Recursive Call: return F2(x - 1); → return F2(1);
 F2(1) Execution:
 Within F2(1):
 Call to F3();
 F2 calls F3.
 F3 executes and returns 5.
 Control returns to F2(1).
 Conditional Check: if (x == 1)
 x = 1, so x == 1 is true.
 Call to F1();
 F2(1) calls F1.
 F1 executes and returns
1.
 Control returns to F2(1).
 F2(1) returns the value obtained from
F1(), which is 1.
 F2(2) receives the return value from F2(1), which is 1,
and returns it.
o Control returns to main.
4. Third Call in main: F3();
o main calls F3.
o F3 executes and returns 5.
o Control returns to main.
5. main Function Ends:
o main finishes execution and returns 0.
Constructing the Activation Tree:
Based on the execution steps, the activation tree can be constructed as follows:
1. Root Node: main
2. main Calls:
o First Child: F1
 Returns to main.
o Second Child: F2(2)
 F2(2) Calls:
 Child: F3
 Returns to F2(2).
 Since x != 1, F2(2) Calls:
 Child: F2(1)
 F2(1) Calls:
 Child: F3
 Returns to F2(1).
 Since x == 1, F2(1) Calls:
 Child: F1
 Returns to F2(1).
 F2(1) Returns to F2(2).
 F2(2) Returns to main.
o Third Child: F3
 Returns to main.
Visualization of the Activation Tree:
EX – 14
Question:
In GATE 2023, a question was asked involving a recursive function foo. The main function
calls foo with the arguments (15, 15, 10). The function is defined as follows:

Question:
What is the output of this program?

Answer:
Understanding the Function foo
The recursive function foo operates based on the values of x, y, and q, and it uses a
combination of recursive calls and conditions to compute a result.
 Base Case:
o If both x <= 0 and y <= 0, return q.
 Recursive Cases:
o If x <= 0, call foo(x, y - q, q).
o If y <= 0, call foo(x - q, y, q).
o Otherwise, return the sum of two recursive calls:
 foo(y - q, x, q)
 foo(x - q, y, q)
Initial Call:
 foo(15, 15, 10)
Step-by-Step Execution:
We will trace the recursive calls and compute the return values.

Call Stack:
1. First Call: foo(15, 15, 10)
o x = 15, y = 15, q = 10
o None of the base cases are met.
o Proceed to the last return statement:
 return foo(15 - 10, 15, 10) + foo(15 - 10, 15, 10)
 The recursive calls are:
 foo(5, 15, 10)
 foo(5, 15, 10)
2. Second Call: foo(5, 15, 10)
o x = 5, y = 15, q = 10
o None of the base cases are met.
o Recursive calls:
 foo(15 - 10, 5, 10) → foo(5, 5, 10)
 foo(5 - 10, 15, 10) → foo(-5, 15, 10)
3. Third Call: foo(5, 5, 10)
o x = 5, y = 5, q = 10
o None of the base cases are met.
o Recursive calls:
 foo(5 - 10, 5, 10) → foo(-5, 5, 10)
 foo(5 - 10, 5, 10) → foo(-5, 5, 10)
4. Fourth Call: foo(-5, 15, 10)
o x = -5, y = 15, q = 10
o Since x <= 0, we proceed to:
 return foo(-5, 15 - 10, 10) → foo(-5, 5, 10)
5. Fifth Call: foo(-5, 5, 10)
o x = -5, y = 5, q = 10
o Since x <= 0, we proceed to:
 return foo(-5, 5 - 10, 10) → foo(-5, -5, 10)
6. Sixth Call: foo(-5, -5, 10)
o x = -5, y = -5, q = 10
o Both x <= 0 and y <= 0, so we return q:
 Return 10

Computing Return Values:


 Sixth Call Returns: 10
 Fifth Call Returns: 10 (from Sixth Call)
 Fourth Call Returns: 10 (from Fifth Call)
 Third Call Returns: 10 + 10 = 20 (sum of two Fifth Calls)
 Second Call Returns: 20 + 10 = 30 (Third Call + Fourth Call)
 First Call Returns: 30 + 30 = 60 (sum of two Second Calls)

Conclusion:
 The output of the program is 60.
EX – 15
Recursive Function calc and Global Counter
Question:
In GATE 2018, a question was asked involving a recursive function calc and a global variable
counter.
The program is as follows:
#include <stdio.h>
int counter = 0;
int calc(int a, int b) {
int c;
counter++;
if (b == 3)
return a * a * a;
else {
c = calc(a, b / 3);
return c * c * c;
}
}
int main() {
calc(4, 81);
printf("%d", counter);
return 0;
}

Question:
What is the output of this program?

Answer:
Understanding the Function calc
 The function calc is a recursive function that increments a global counter variable
each time it is called.
 The base case is when b == 3, in which case it returns a * a * a.
 Otherwise, it recursively calls calc(a, b / 3) and then returns c * c * c, where c is the
result of the recursive call.
Initial Call:
 The main function calls calc(4, 81).
 After the call, it prints the value of counter.
Objective:
 Determine the value of counter after calc(4, 81) is executed.
Step-by-Step Execution:
We need to trace the recursive calls and increment the counter each time calc is called.

Call Stack:
1. First Call: calc(4, 81)
o a = 4, b = 81
o Increment counter to 1.
o Since b != 3, proceed to the else block.
o Compute c = calc(4, 81 / 3) → calc(4, 27)
o After the recursive call, return c * c * c.
2. Second Call: calc(4, 27)
o a = 4, b = 27
o Increment counter to 2.
o Since b != 3, proceed to the else block.
o Compute c = calc(4, 27 / 3) → calc(4, 9)
o After the recursive call, return c * c * c.
3. Third Call: calc(4, 9)
o a = 4, b = 9
o Increment counter to 3.
o Since b != 3, proceed to the else block.
o Compute c = calc(4, 9 / 3) → calc(4, 3)
o After the recursive call, return c * c * c.
4. Fourth Call: calc(4, 3)
o a = 4, b = 3
o Increment counter to 4.
o Since b == 3, return a * a * a → 4 * 4 * 4 → 64.
Now, we can compute the return values while unwinding the recursion.

Unwinding the Recursion:


 Third Call (calc(4, 9)):
o Received c = 64 from the recursive call.
o Compute c * c * c → 64 * 64 * 64 → 262144.
o Return 262144.
 Second Call (calc(4, 27)):
o Received c = 262144.
o Compute c * c * c → 262144 * 262144 * 262144 → A very large number.
o Return this large number.
 First Call (calc(4, 81)):
o Received c from the second call (a very large number).
o Compute c * c * c → An even larger number.
o Return this value.
Note: The actual values are extremely large and not needed since the program only prints
the counter.

Final Counter Value:


 The counter was incremented each time calc was called.
 Total number of times calc was called: 4.

Conclusion:
 The output of the program is the value of counter, which is 4.
EX – 16
In the GATE 2017 exam, a question was asked about the output of a C program involving two
recursive functions: FEN1 and FEN2. The functions are defined as follows:
#include <stdio.h>
void FEN1(int n) {
if (n == 0)
return;
else {
printf("%d ", n);
FEN2(n - 2);
printf("%d ", n);
}
}
void FEN2(int n) {
if (n == 0)
return;
else {
printf("%d ", n);
FEN1(++n);
printf("%d ", n);
}
}
int main() {
FEN1(5);
return 0;
}
Question:
What is the output when the function FEN1 is called with the argument 5? That is, what does
FEN1(5) print when executed?
Answer:
Understanding the Functions:
1. Function FEN1:
o Base Case: If n == 0, return.
o Recursive Case:
 Print n.
 Call FEN2(n - 2).
 Print n.
2. Function FEN2:
o Base Case: If n == 0, return.
o Recursive Case:
 Print n.
 Increment n (++n), then call FEN1(n).
 Print n.
Objective:
Determine the sequence of numbers printed when FEN1(5) is called.
Step-by-Step Execution:
Let's trace the execution of the program step by step, keeping track of the function calls and
the values of n.

1. Call FEN1(5)
 n=5
 Check: n == 0? No
 Action:
o Print: 5
o Call: FEN2(5 - 2) → FEN2(3)
o After FEN2(3) returns, Print: 5
2. Call FEN2(3)
 n=3
 Check: n == 0? No
 Action:
o Print: 3
o Increment n: ++n → n = 4
o Call: FEN1(4)
o After FEN1(4) returns, Print: 4

3. Call FEN1(4)
 n=4
 Check: n == 0? No
 Action:
o Print: 4
o Call: FEN2(4 - 2) → FEN2(2)
o After FEN2(2) returns, Print: 4

4. Call FEN2(2)
 n=2
 Check: n == 0? No
 Action:
o Print: 2
o Increment n: ++n → n = 3
o Call: FEN1(3)
o After FEN1(3) returns, Print: 3

5. Call FEN1(3)
 n=3
 Check: n == 0? No
 Action:
o Print: 3
o Call: FEN2(3 - 2) → FEN2(1)
o After FEN2(1) returns, Print: 3

6. Call FEN2(1)
 n=1
 Check: n == 0? No
 Action:
o Print: 1
o Increment n: ++n → n = 2
o Call: FEN1(2)
o After FEN1(2) returns, Print: 2

7. Call FEN1(2)
 n=2
 Check: n == 0? No
 Action:
o Print: 2
o Call: FEN2(2 - 2) → FEN2(0)
o After FEN2(0) returns, Print: 2

8. Call FEN2(0)
 n=0
 Check: n == 0? Yes
 Action: Return immediately.
 Back to FEN1(2), Print: 2

Back to FEN2(1), Print: 2


 After FEN1(2) returns, n in FEN2(1) is 2 (after ++n).

Back to FEN1(3), Print: 3


 After FEN2(1) returns to FEN1(3).

Back to FEN2(2), Print: 3


 After FEN1(3) returns, n in FEN2(2) is 3 (after ++n).

Back to FEN1(4), Print: 4


 After FEN2(2) returns to FEN1(4).

Back to FEN2(3), Print: 4


 After FEN1(4) returns, n in FEN2(3) is 4 (after ++n).

Back to FEN1(5), Print: 5


 After FEN2(3) returns to FEN1(5).

Sequence of Printed Numbers:


Let's list all the numbers printed in the order they appear:
1. Print from FEN1(5): 5
2. Print from FEN2(3): 3
3. Print from FEN1(4): 4
4. Print from FEN2(2): 2
5. Print from FEN1(3): 3
6. Print from FEN2(1): 1
7. Print from FEN1(2): 2
8. Print after FEN2(0) returns in FEN1(2): 2
9. Print after FEN1(2) returns in FEN2(1): 2
10. Print after FEN2(1) returns in FEN1(3): 3
11. Print after FEN1(3) returns in FEN2(2): 3
12. Print after FEN2(2) returns in FEN1(4): 4
13. Print after FEN1(4) returns in FEN2(3): 4
14. Print after FEN2(3) returns in FEN1(5): 5
Final Output:
Combining all the printed numbers:

Detailed Explanation:
Let's align the printed numbers with their corresponding function calls for clarity.
 Call FEN1(5)
o Print: 5
o Call FEN2(3)
 Call FEN2(3)
o Print: 3
o Increment n to 4, call FEN1(4)
 Call FEN1(4)
o Print: 4
o Call FEN2(2)
 Call FEN2(2)
o Print: 2
o Increment n to 3, call FEN1(3)
 Call FEN1(3)
o Print: 3
o Call FEN2(1)
 Call FEN2(1)
o Print: 1
o Increment n to 2, call FEN1(2)
 Call FEN1(2)
o Print: 2
o Call FEN2(0) (which returns immediately)
o Print: 2 (after FEN2(0) returns)
 Back to FEN2(1)
o Print: 2 (after FEN1(2) returns)
 Back to FEN1(3)
o Print: 3 (after FEN2(1) returns)
 Back to FEN2(2)
o Print: 3 (after FEN1(3) returns)
 Back to FEN1(4)
o Print: 4 (after FEN2(2) returns)
 Back to FEN2(3)
o Print: 4 (after FEN1(4) returns)
 Back to FEN1(5)
o Print: 5 (after FEN2(3) returns)

Option Matching:
Assuming the options in the original question were:
1. Option A: 5 3 4 2 3 1 2 2 2 3 3 4 4 5
2. Option B: 5 3 4 2 3 1 2 2 2 3 3 4 4
3. Option C: 5 3 4 2 3 1 2 3 4 5
4. Option D: 5 4 3 2 1 5
Correct Answer: Option A
Understanding Pointers in C Programming
In this discussion, we will cover the concept of pointers in C programming. The topics are
arranged as follows:
1. Basics of Pointers
o Declaration of Pointers
o Assigning Pointers to Variables
o Passing Address of a Variable
o Pointer Assignment
2. Common Pointer Problems
o Dangling Pointers
o Memory Leakage Problem
o Uninitialized Pointer Problem
o Null Pointer Dereference Problem
3. Advanced Pointer Concepts
o Pointer to Pointer
o Pointer to One-Dimensional Array
o Pointer to Two-Dimensional Array
o Pointer to Three-Dimensional Array
o Pointer to String
o Pointer to Function
o Pointer to Structures
o Array of Pointers
o Array of Function Pointers
Note: This discussion focuses on the basics of pointers.

Basics of Pointers
Memory Organization in Turbo C 3.0
 Default Program Size: In Turbo C 3.0, by default, every program occupies 64
kilobytes (KB) of memory.
 Memory Segmentation:
o The total memory of 1 megabyte (MB) is divided into 16 segments.
o Each segment is 64 KB in size.
o Segments are labeled from 0x0 to 0xF in hexadecimal (0 to 15).
o Each segment serves a specific purpose (e.g., data segment, code segment,
graphics segment).
 Data Segment:
o Variables are generally stored in the data segment.
o Addresses in the data segment start from 0x0000 up to 0xFFFF (0 to 65,535
in decimal).
Declaring Variables and Understanding Addresses
Example:

 The compiler allocates 2 bytes of memory for the integer variable i (assuming int
size is 2 bytes).
 The variable i is stored at a base address (e.g., 100) and the next byte (address
101).
 The value 265 is stored in binary format across these two bytes.
Printing Addresses:
 To print the address of a variable, use the & operator.
 Use %u format specifier for decimal addresses or %x for hexadecimal.

printf("%u", &i); // Prints the base address of 'i' in decimal.


 Even if a variable occupies multiple bytes, the address refers to its starting byte.

 Pointers
 Definition:
o A pointer is a variable that holds the address of another variable.
o It stores the base address of the variable, regardless of its size.
 Declaration Syntax:
 data_type specifies the type of data the pointer will point to.
 * is the indirection operator used to declare a pointer.
 pointer_name is the name of the pointer variable.

 Examples:

 Assigning Address to Pointers:

 The pointer variable stores the base address of the respective variable.
 Accessing Data Using Pointers:
 Use the dereference operator * to access or modify the value at the address stored
in a pointer.
printf("%d", *p); // Accesses the value of 'i' through pointer 'p'
o Accessing a variable directly by its name is called direct accessing.
o Accessing a variable through a pointer is called indirect accessing.
Examples:
1. Integer Pointer:

 p holds the base address of i.


 *p accesses the value stored at that address.

2. Character Pointer:
3. Float Pointer:

 Note: The pointer knows how many bytes to access based on its data type.

Pointer Assignment
 Concept:
o Pointer assignment involves assigning the value of one pointer to another,
making both pointers point to the same memory location.
 Example:

 Both p and q hold the address of i.

 Accessing Values:

 Modifying Pointer Target:

 Now p points to j (value 30), while q still points to i (value 20).

 Accessing Updated Values:

Example Program: Pointer Assignment


Program Code:
#include <stdio.h>
int main() {
int i = 0, j = 1;
int *p, *q;
p = &i; // 'p' points to 'i'
q = &j; // 'q' points to 'j'
p = q; // Pointer assignment: 'p' now points to where 'q' points
*p = 2; // Modifies the value at the address 'p' points to 2
printf("%d %d\n", i, j); // Outputs: 0 2
return 0;
}

Explanation:
 p initially points to i.
 After p = q;, p points to j.
 *p = 2; updates the value of j to 2.
 i remains unchanged.

Passing Address of a Variable


 Concept:
o Passing the address of a variable to a function allows the function to modify
the original variable.
 Example:
 Explanation:
 &i passes the address of i to the function f.
 Inside f, *p accesses the value of i.
 The value of i is updated by adding n (which is 5).
 The change reflects in the original variable i.

Another Example: Swapping Values


Function Definitions:

Main Function:

Explanation:
 First Function Call (f1(a, b)):
o Values of a and b are passed.
o Swap occurs within the function scope.
o Original variables a and b in main remain unchanged.
 Second Function Call (f2(&a, &b)):
o Addresses of a and b are passed.
o Pointers a and b in f2 are local copies.
o Swapping pointers a and b does not affect the original addresses.
o Values of a and b in main remain unchanged.
 Result Calculation (c - b - a):
o c is 40.
o b is 30.
o a is 20.
o Calculation: 40 - 30 - 20 = -10.

Example: Passing Address of a Variable


Program Code:

Explanation:
 The function f modifies the original variable i by accessing it through its pointer.
Another Example: Pointer Manipulation
Program Code:
#include <stdio.h>
void f1(int a, int b) {
int c;
c = a;
a = b;
b = c;
}
void f2(int *a, int *b) {
int c;
c = *a;
*a = *b;
*b = c;
}
int main() {
int a = 4, b = 5, c = 6;
f1(a, b); // Passing values
f2(&b, &c); // Passing addresses
printf("%d", c - b - a); // Outputs: -5
return 0;
}

Explanation:
 First Function Call (f1(a, b)):
o Swaps local copies of a and b.
o Original a and b in main are unchanged.
 Second Function Call (f2(&b, &c)):
o Swaps the values of b and c in main.
o After swapping, b becomes 6, and c becomes 5.
 Result Calculation (c - b - a):
o c is now 5.
o b is now 6.
o a remains 4.
o Calculation: 5 - 6 - 4 = -5.

Visualizing Memory Arrangement in Turbo C 3.0

Memory Segmentation
In Turbo C 3.0, the total memory available is 1 megabyte (1 MB). This memory is divided
into 16 segments, each of 64 kilobytes (64 KB) in size.
 Total Memory: 1 MB
 Number of Segments: 16
 Segment Size: 64 KB each
 Segment Labels: From 0x0 to 0xF in hexadecimal (which corresponds to 0 to 15 in
decimal)
Diagram

Each segment serves a specific purpose. For example:


 Data Segment: Where variables are stored.
 Code Segment: Where the program code is stored.
 Graphics Segment: Used for graphics-related data.

Variable Storage
Variables are stored in the data segment. Each variable occupies memory based on its
data type size.
Data Types and Their Sizes:
 int: 2 bytes
 char: 1 byte
 float: 4 bytes
Example Variables:
Let's consider the following variable declarations:

Memory Allocation:
 Variable i (int):
o Occupies 2 bytes.
o Let's assume it is stored starting at address 100.
o Addresses used: 100, 101.
 Variable a (char):
o Occupies 1 byte.
o Let's assume it is stored at address 200.
 Variable r (float):
o Occupies 4 bytes.
o Let's assume it is stored starting at address 300.
o Addresses used: 300, 301, 302, 303.
GATE Questions: Understanding Pointer Problems in C
Programming
In this discussion, we will explore four common pointer-related problems in C
programming:
1. Dangling Pointer Problem
2. Uninitialized Pointer Problem
3. Null Pointer Dereference Problem
4. Memory Leakage Problem
For each problem, we will provide explanations, code examples, and visualize the memory
allocation involved to help you understand how these issues arise and how to prevent
them.

Dangling Pointer Problem


What is a Dangling Pointer?
A dangling pointer arises when a pointer references a memory location that has been
deallocated or is no longer valid. This typically happens when a function returns the
address of a local (automatic) variable, and the variable goes out of scope after the
function exits.
Code Example
Explanation
 Step 1: The function function() declares a local variable x with a value of 25.
 Step 2: It returns the address of x using &x.
 Step 3: In main(), pointer p receives this address.
 Step 4: After function() exits, x goes out of scope, and its memory is reclaimed.
 Step 5: p now points to a memory location that is no longer valid.
 Step 6: Dereferencing *p leads to undefined behavior because p is a dangling
pointer.

Preventing Dangling Pointers


To prevent this issue, avoid returning the address of local variables. If you need to return
data from a function, consider:
 Returning the value directly.
 Allocating memory dynamically (e.g., using malloc) and returning the pointer.
 Passing a pointer to the function to store the result.
Corrected Code
#include <stdio.h>
#include <stdlib.h>
int* function() {
int *x = (int *)malloc(sizeof(int)); // Allocate memory on the heap
*x = 25;
return x;
}
int main() {
int *p;
p = function(); // 'p' points to valid memory on the heap
printf("%d\n", *p); // Outputs: 25
free(p); // Free the allocated memory
return 0;
}

Uninitialized Pointer Problem


What is an Uninitialized Pointer?
An uninitialized pointer is a pointer variable that has not been assigned any address. It
contains a garbage (indeterminate) value, which could be any memory address, including
those used by the operating system or other programs.
Code Example
#include <stdio.h>
int main() {
int *p; // Uninitialized pointer
*p = 20; // Attempt to assign value to an unknown address
printf("%d\n", *p);
return 0;
}

Explanation
 Step 1: Pointer p is declared but not initialized.
 Step 2: p contains a garbage address.
 Step 3: Assigning *p = 20 attempts to write to an unknown memory location.
 Step 4: This can lead to undefined behavior, including program crashes or
corruption of data.
Preventing Uninitialized Pointer Problems
Always initialize pointers before using them:
 Assign them the address of a valid variable.
 Allocate memory dynamically using malloc.
 Set them to NULL and check before use.
Corrected Code
Null Pointer Dereference Problem
What is a Null Pointer Dereference?
A null pointer is a pointer that is assigned the value NULL, indicating it points to nothing.
Dereferencing a null pointer (attempting to access the data it points to) leads to undefined
behavior, often resulting in a segmentation fault.
Code Example

Explanation
 Step 1: Pointer p is initialized to NULL.
 Step 2: *p = 10 attempts to write to address 0.
 Step 3: This is invalid and typically results in a runtime error.

Preventing Null Pointer Dereference


Before dereferencing a pointer, always check if it is not NULL:

Memory Leakage Problem


What is a Memory Leak?
A memory leak occurs when a program allocates memory dynamically (e.g., using malloc)
but fails to release it back to the system using free. Over time, this can consume all
available memory, leading to performance issues or program crashes.
Code Example
#include <stdio.h>
#include <stdlib.h>
int main() {
int *arr;
arr = (int *)malloc(5 * sizeof(int)); // Allocate memory for an array of 5 integers
if (arr == NULL) {
// Handle allocation failure
return 1;
}
// Use the array
// Forgot to free the memory
return 0;
}
Explanation
 Step 1: Memory is allocated on the heap for an array of 5 integers.
 Step 2: The program uses the array (code omitted).
 Step 3: The program ends without freeing the allocated memory.
 Step 4: The memory remains allocated, leading to a memory leak
Preventing Memory Leaks
Always pair every malloc (or calloc, realloc) with a corresponding free when the memory
is no longer needed.
Corrected Code

Summary
 Dangling Pointer Problem:
o Occurs when a pointer references deallocated memory.
o Prevented by avoiding returning addresses of local variables.
 Uninitialized Pointer Problem:
o Using pointers that haven't been assigned a valid memory address.
o Prevented by initializing pointers before use.
 Null Pointer Dereference Problem:
o Dereferencing a pointer set to NULL.
o Prevented by checking if a pointer is NULL before dereferencing.
 Memory Leakage Problem:
o Failing to free dynamically allocated memory.
o Prevented by ensuring every malloc has a corresponding free.
GATE Questions: Understanding Signed and Unsigned
Integers in Pointer Operations
1. Basics of Signed and Unsigned Integers
Signed Integers
 Definition: Can represent both negative and positive numbers.
 Range for 8-bit signed integer (char): -128 to 127.
 Representation: Uses the two's complement method for negative numbers.
Unsigned Integers
 Definition: Can represent only non-negative numbers (zero and positive numbers).
 Range for 8-bit unsigned integer (unsigned char): 0 to 255.
 Representation: All bits are used to represent the magnitude of the number.
Two's Complement Representation
 Used for signed integers to represent negative numbers.
 How it works:
1. Positive Numbers: Represented in binary as usual.
2. Negative Numbers: Invert all bits of the positive number (one's complement) and
add 1.

Pointer Operations with Different Data Types


 Assigning an int* to a char*: Allowed in C, but may generate a warning.
 Dereferencing a char* that points to an int: Reads only one byte (8 bits) from the
address.
 Interpretation of Data:
o Signed char*: Interprets the byte as a signed value (-128 to 127).
o Unsigned char*: Interprets the byte as an unsigned value (0 to 255).
Detailed Analysis of Code Examples
Example 1: Reading an int Using a char* Pointer

1. Binary Representation of 265:

2. Pointer Assignment:
o p = (char *)&i;
o p now points to the address of i, but treats it as a char*.
3. Dereferencing p:
o j = *p;
o *p reads one byte from address 1000.
o Value at *p: 0000 1001 (binary) which is 9 in decimal.
4. Output:
printf("%d\n", j); prints 9.
Explanation:
 Since p is a char*, it reads only the first byte of i.
 The first byte (low byte) of i contains the value 9.
 The high byte (containing 1) is ignored in this operation.
 Also MSB = 0 THUS 0 * -1 = 0
Example 2: Signed Interpretation of a Byte

Binary Representation of 255:

o High byte: 0000 0000 (0 in decimal)


o Low byte: 1111 1111 (255 in decimal)

Pointer Assignment:
 p = (char *)&i;
Dereferencing p:
 j = *p;
 *p reads one byte from address 1000.
 Value at *p: 1111 1111 (binary)
Interpreting the Byte:
 As a Signed char:
o Range: -128 to 127
o 1111 1111 represents -1 in two's complement.
Output:
 printf("%d\n", j); prints -1.
Example 3: Unsigned Interpretation of a Byte

1. Memory Representation of i:
o Same as in Example 2.
2. Pointer Assignment:
o p = (unsigned char *)&i;
3. Dereferencing p:
o j = *p;
o *p reads one byte from address 1000.
o Value at *p: 1111 1111 (binary)
4. Interpreting the Byte:
o As an Unsigned char:
 Range: 0 to 255
 1111 1111 represents 255.
5. Output:
o printf("%d\n", j); prints 255.
Explanation:
 By declaring p as an unsigned char*, we tell the compiler to interpret the byte as
an unsigned value.
 Therefore, 1111 1111 is interpreted as 255.
GATE Questions:
Pointer Assignment and Value Manipulation (GATE 2014)
Problem Statement
Consider the following C code:
#include <stdio.h>
int main() {
int i;
int *p = &i; // Pointer declaration and initialization
printf("Enter one value: ");
scanf("%d", p); // Reading value into the location pointed by 'p'
i = i + 5;
printf("%d\n", i);
return 0;
}
Options
1. Compile-time error.
2. Run-time error.
3. On execution, the value printed is 5 more than the address of i.
4. The value printed is 5 more than the value which we entered.

Answer and Explanation


Correct Option: 4. The value printed is 5 more than the value which we entered.
Explanation:
1. Variable Declarations:
o int i; declares an integer variable i.
o int *p = &i; declares a pointer p and initializes it with the address of i.
2. Input Statement:
o scanf("%d", p); reads an integer from the user and stores it in the location
pointed to by p.
o Since p points to i, the input value is stored in i.
3. Value Manipulation:
o i = i + 5; increments i by 5.
4. Output Statement:
o printf("%d\n", i); prints the value of i.
5. Behavior:
o The program reads a value, adds 5 to it, and prints the result.
o If the user inputs 20, the program outputs 25.
6. No Errors:
o There are no compile-time or run-time errors in this code.
o The code is syntactically and logically correct.
Key Concepts:
 Pointer Initialization:
o int *p = &i; initializes the pointer p with the address of i.
 Using Pointers with scanf:
o scanf requires the address of the variable where the input value should be
stored.
o Passing p (which is &i) fulfills this requirement.
Question 2: Passing Address to Functions and Modifying Values
Problem Statement
Consider the following C code:

What is the output of the program?


Answer and Explanation
Correct Answer: 30
Explanation:
1. Variable Declarations in main:
o int i = 5;
o int j = 10;
2. Function Call:
o f(&i, j); passes the address of i and the value of j to function f.
3. Inside Function f:
o m = m + 5; modifies the local copy of m.
 m was 10, now m = 10 + 5 = 15.
o *p = *p + m; modifies the value pointed to by p.
 *p was 5, now *p = 5 + 15 = 20.
4. Back in main:
o i is now 20.
o j remains 10 (since only a copy of j was passed to f).
o printf("%d\n", i + j); outputs 20 + 10 = 30.
Key Concepts:
 Passing by Address vs. Passing by Value:
o int *p receives the address of i, allowing modification of i in main.
o int m receives a copy of j, so changes to m do not affect j in main.
 Modifying Values via Pointers:
o Using *p allows the function to modify the value of i directly.

Question 3: Pointer Dereferencing and Value Updates


Problem Statement
Consider the following C function and its call:

What is the output of the program?


Answer and Explanation

Correct Answer: 1 0
Explanation:
1. Function Call:
o print(1, 1); calls the function with x = 1 and y = 1.
2. Inside Function print:
o x = 0; sets x to 0.
o ptr = &x; sets ptr to point to x.
o y = *ptr; sets y to the value pointed to by ptr (which is x).
 Now, y = 0.
o *ptr = 1; updates the value pointed to by ptr to 1.
 x is now 1.
3. Printing Values:
o printf("%d %d\n", x, y); prints x and y.
 x=1
 y=0

Key Concepts:
 Order of Operations:
o The assignment y = *ptr; happens before *ptr = 1;.
 Pointer Dereferencing:
o *ptr accesses the value of x through the pointer.
Question 4: Analyzing Functions for Pointer Problems
Problem Statement
Consider the following three functions:

Question
Which of the above three functions are likely to cause problems with pointers?
Options
1. Only P3
2. P1 and P3
3. P1 and P2
4. All three functions
Answer and Explanation
Correct Option: 3. P1 and P2
Explanation:
Function P1:
 Issue: Returning the address of a local variable (x).
 Problem: Once the function returns, x goes out of scope, and the returned pointer
points to invalid memory.
 Type of Problem: Dangling Pointer
Function P2:
 Issue: Using an uninitialized pointer (px) and assigning a value to the location it
points to.
 Problem: px contains a garbage address; dereferencing it leads to undefined
behavior.
 Type of Problem: Uninitialized Pointer (Wild Pointer)
Function P3:
 Issue: Allocates memory using malloc and returns the pointer.
 Problem: No immediate issue; the caller is responsible for freeing the allocated
memory.
 Type of Problem: No Problem (Assuming the caller frees the memory later to
prevent a memory leak)
Key Concepts:
 Dangling Pointer:
o Occurs when a pointer points to a memory location that has been
deallocated.
o Returning the address of a local variable leads to a dangling pointer.
 Uninitialized Pointer (Wild Pointer):
o A pointer that has not been assigned a valid memory address.
o Using such a pointer leads to undefined behavior.
 Proper Memory Allocation:
o Using malloc allocates memory on the heap.
o It's safe to return pointers to heap-allocated memory, but the caller must
free it when done.
Problem 5: Recursive Function Passing Value and Address

Question: What is the output of the program?


Answer
The output of the program is: 8

Explanation
Let's break down the code step by step and visualize the recursive calls.
Function fun Overview
 Parameters:
o int n: An integer value.
o int *p: A pointer to an integer.
 Functionality:
o If n <= 1, set *p = 1 and return 1.
o Otherwise, recursively call fun(n - 1, p).
o Update f = t + *p (where t is the result of the recursive call).
o Set *p = t.
o Return f.
Initial Call
 In main(), x is initialized to 15.
 We call fun(5, &x).
Visualization of Recursive Calls
We will visualize the recursion using a call stack and keep track of variables n, *p, t, f at
each level.
Call Stack and Variables
Let's represent each function call as a frame in the call stack:
1. Call fun(5, &x)
o n=5
o *p initially points to x (value 15)
2. Call fun(4, &x)
o n=4
o *p still points to x
3. Call fun(3, &x)
o n=3
o *p still points to x
4. Call fun(2, &x)
o n=2
o *p still points to x
5. Call fun(1, &x)
o n=1
o Base case is met (n <= 1)
o *p = 1 (updates x to 1)
o Return 1
Now, we'll unwind the recursion and compute t, f, and update *p at each level.
Unwinding the Recursion
At n = 2:
 t = fun(1, p) returns 1
 *p after base case is 1

 f = t + *p ⇒ f = 1 + 1 = 2

 Update *p = t ⇒ *p = 1 (no change)


 Return f = 2
At n = 3:
 t = fun(2, p) returns 2
 *p from previous level is 1

 f = t + *p ⇒ f = 2 + 1 = 3

 Update *p = t ⇒ *p = 2
 Return f = 3
At n = 4:
 t = fun(3, p) returns 3
 *p from previous level is 2

 f = t + *p ⇒ f = 3 + 2 = 5

 Update *p = t ⇒ *p = 3
 Return f = 5
At n = 5:
 t = fun(4, p) returns 5
 *p from previous level is 3

 f = t + *p ⇒ f = 5 + 3 = 8

 Update *p = t ⇒ *p = 5
 Return f = 8
Final Output:
 The result in main() is 8, so printf("%d\n", result); outputs 8.
Understanding Variable *p Updates
 *p is updated at each level to hold the value of t from the previous recursive call.
 This effectively shifts the values, similar to how Fibonacci numbers work.

Visualization as a Tree
Here's a visualization of the recursive calls:

Problem 6: Global and Local Variables with Functions


Problem Statement
Consider the following C code:
#include <stdio.h>
int x; // Global variable
void Q(int z) {
z += x;
printf("%d ", z);
}

void P(int *y) {


int x = *y + 2; // Local variable x
*y = x - 1;
Q(x);
printf("%d ", x);
}

int main() {
x = 5;
P(&x);
printf("%d\n", x);
return 0;
}
Question: What is the output of the program?
Answer
The output of the program is: 13 7 6

Explanation
Let's analyze the program step by step.
Variable Declarations
 Global Variable:
o int x; declared globally.
 Local Variables:
o In P: int x (local to P).
o In Q: int z (parameter).
o In main: uses global x.
Execution Flow
1. In main():
o x = 5; sets global x to 5.
o Calls P(&x); passing the address of global x.
2. In P(int *y):
o int x = *y + 2;
 *y is 5 (value of global x).
 Local x in P is 5 + 2 = 7.
o *y = x - 1;
 *y is updated to 7 - 1 = 6.
 Global x is now 6.
o Calls Q(x); passing local x (7).
o printf("%d ", x); prints 7.
3. In Q(int z):
o z += x;
 x here refers to the global x (6).
 z becomes 7 + 6 = 13.
o printf("%d ", z); prints 13.
4. Back in main():
o After P(&x); completes, printf("%d\n", x); prints global x, which is 6.

Output Sequence

1. printf("%d ", z); in Q ⇒ 13

2. printf("%d ", x); in P ⇒ 7

3. printf("%d\n", x); in main ⇒ 6


Thus, the output is: 13 7 6
Problem 7: Type Checking and Pointer Operations
Problem Statement
Consider the following code fragment:

Question:
Which of the following expressions, when placed in the blank, will not give a type
checking error?
Options
1. f(i, *p);
2. f(s, *s);
3. f(i, *s);
4. i = f(i);

Corrected Answer and Explanation


Analyzing Each Option
1. Option 1: f(i, *p);

o i is int ⇒ matches the first parameter.

o *p dereferences p (which is short *) to get short s ⇒ matches the second


parameter.
o No type checking error. ✅
2. Option 2: f(s, *s);
o s is short.
o *s attempts to dereference s, but s is a short, not a pointer.
o Type checking error: Cannot dereference a non-pointer type. ❌
3. Option 3: f(i, *s);

o i is int ⇒ matches the first parameter.


o *s attempts to dereference s, but s is a short, not a pointer.
o Type checking error: Cannot dereference a non-pointer type. ❌
4. Option 4: i = f(i);
o f is declared as void f(int, short); — it expects two arguments.
o The function returns void, but we are trying to assign its return value to i.
o Type checking error: Incorrect number of arguments and assigning void to
int. ❌
Pointer to Pointer
Declaring and Using Double Pointers
Step-by-Step Example
Step 1: Declare an Integer Variable

Step 2: Declare a Pointer to Integer

Step 3: Declare a Pointer to Pointer to Integer:

Memory Representation

Indirect Access via q:

Understanding the Levels of Indirection


 *p gives the value of i.
 *q gives the value of p (which is the address of i).
 **q gives the value at the address pointed to by p (which is the value of i).
Triple Pointers and Beyond
You can extend this concept to multiple levels.
 Triple Pointer:
int ***r = &q; // 'r' is a pointer to a pointer to a pointer to an int
Triple Indirection:
 Add One Extra * to the Data Type:
 Each level of indirection adds one *.

Practical Examples
Example 1: Modifying a Value Using Double Pointer

Explanation:
 modifyValue takes a double pointer q.
 **q = 20; sets the value of i to 20.
Example 2: Dynamic Memory Allocation with Double Pointers
#include <stdio.h>
#include <stdlib.h>

void allocateMemory(int **ptr) {


*ptr = (int *)malloc(sizeof(int));
if (*ptr != NULL) {
**ptr = 30;
}
}
int main() {
int *p = NULL;
allocateMemory(&p);

if (p != NULL) {
printf("Value: %d\n", *p); // Outputs: 30
free(p);
} else {
printf("Memory allocation failed.\n");
}
return 0;
}

Explanation:
 allocateMemory allocates memory for an integer and assigns it to the pointer
passed by reference.
 Using a double pointer allows the function to modify the original pointer in main.
Exercise – 1
Code

Memory Representation

Modifying the Value of 'i' Using Pointer 'p'


 *p = 35; — Dereferences p and sets the value at that address to 35.
 Since p points to i, i is now 35.
Printing Values
 printf("%d %d %d\n", *p, **q, i);

o *p — Dereferences p to get the value of i ⇒ 35.

o **q — Dereferences q to get p, then dereferences p to get i ⇒ 35.

o i — Directly accesses i ⇒ 35.


 Output: 35 35 35
Changing Pointer 'p' to Point to 'j' Using Pointer to Pointer 'q'
 *q = &j;
o *q dereferences q to get p.
o Assigns the address of j to p.
o Now, p points to j.
Memory Representation After Reassigning 'p'

Modifying the Value of 'j' Using Pointer 'p'


 *p = 55; — Since p now points to j, this sets j to 55.
Printing Final Values of 'i' and 'j'
 printf("%d %d\n", i, j);

o i ⇒ 35 (unchanged after reassigning p).

o j ⇒ 55 (updated via p).


 Output: 35 55
Final Output

Problem 2
Code
#include <stdio.h>

int f(int x, int *py, int **ppz) {


int y, z;
**ppz += 1; // Increments value pointed to by '*ppz' by 1
z = **ppz; // 'z' gets the updated value
*py += 2; // Increments value pointed to by 'py' by 2
y = *py; // 'y' gets the updated value
x += 3; // Increments 'x' by 3
return x + y + z;
}

int main() {
int c = 4;
int *a;
int **b;

a = &c; // 'a' points to 'c'


b = &a; // 'b' points to 'a'

int result = f(c, a, b);


printf("%d\n", result);
return 0;
}

Memory Representation Before Function Call

Calling Function f
 int result = f(c, a, b);
 Arguments Passed:
o x = 4;

o py = a; (address of c ⇒ 2000)

o ppz = b; (address of a ⇒ 3000)


Inside Function f
 Local Variables:
o int y, z;
 Operations:
1. **ppz += 1;

 *ppz dereferences ppz to get a (3000 ⇒ 2000).


 **ppz dereferences a to get c.
 Increments c by 1: c = 4 + 1 = 5.
2. z = **ppz;
 After increment, **ppz gives c = 5.
 z = 5.
3. *py += 2;
 *py dereferences py to get c.
 Increments c by 2: c = 5 + 2 = 7.
4. y = *py;
 After increment, *py gives c = 7.
 y = 7.
5. x += 3;
 x = 4 + 3 = 7.
6. return x + y + z;
 x + y + z = 7 + 7 + 5 = 19.
Memory Representation After Function Execution

 c has been updated to 7.


Printing the Result
 printf("%d\n", result); — Outputs 19.
Final Output
Problem 3
Code
#include <stdio.h>
int x = 5; // Global variable
void F1(int **q, int z) {
z = **q + x; // 'x' is global variable
printf("%d ", z);
}
void F2(int *p) {
int x; // Local variable in F2
x = *p + 1;
F1(&p, x); // Passing address of 'p' and 'x' to F1
*p = x - 1;
printf("%d ", x);
}
int main() {
x = 10; // Modifies global 'x'
F2(&x); // Passes address of global 'x' to F2
printf("%d\n", x); // Prints global 'x'
return 0;
}

Execution Flow
Step 1: In main Function
 x = 10; — Updates the global x to 10.
 Calls F2(&x); — Passes the address of global x to F2.
Step 2: In F2 Function
 Parameters:
o int *p; — Receives address of global x.
 Local Variable:
o int x; — Shadows the global x within F2.
 Operations:
1. x = *p + 1;
 *p dereferences p to get global x (10).
 x = 10 + 1 = 11; — Local x in F2 is 11.
2. F1(&p, x);
 Passes address of p (&p) and local x (11) to F1.
Step 3: In F1 Function
 Parameters:
o int **q; — Receives address of p from F2.
o int z; — Receives 11 from F2.
 Operations:
1. z = **q + x;
 **q dereferences q twice to get the value pointed to by p, which is
global x (10).
 x refers to the global x (10).
 z = 10 (from **q) + 10 (global x) = 20;
2. printf("%d ", z);
 Outputs 20 .
Step 4: Back in F2 Function
 Continued Operations:
3. *p = x - 1;
 x is local x in F2 (11).
 *p points to global x.
 Updates global x: *p = 11 - 1 = 10; — Global x is now 10.
4. printf("%d ", x);
 Prints local x in F2: 11 .
Step 5: Back in main Function
 Continued Operations:
5. printf("%d\n", x);
 Prints global x, which has been updated to 10.

Final Output
Pointers in C: Understanding Different Types and Their
Usage

In this guide, we will discuss three different types of pointers:


1. Pointer to Array Element
2. Pointer to One-Dimensional Array
3. Pointer to Two-Dimensional Array
To understand these concepts, we'll delve into basic concepts about one-dimensional
arrays, two-dimensional arrays, and three-dimensional arrays.

1. Pointer to Array Element


A pointer to an array element is a pointer that points directly to a specific element within
an array.
Example:
Consider the following array declaration:

 The array a has 5 integer elements.


 Assume the base address (starting memory address) of the array is 100.
 Each integer occupies 2 bytes of memory.
 The addresses of the elements are:
o a[0] at 100
o a[1] at 102
o a[2] at 104
o a[3] at 106
o a[4] at 108
Important Points:
1. Array Name as Pointer:
o The array name a holds the base address of the array.
o It can be considered as a constant pointer to the first element.
2. Constant Pointer:
o Array name is a constant pointer, meaning its value (the base address)
cannot be changed.
o Attempting to modify it will result in a compiler error.

3. Variable Pointer:
 To navigate through the array elements, use a variable pointer.

 You can modify P to point to other elements.

4. Pointer Arithmetic:
 When you increment a pointer, it moves to the next memory location
considering the size of the data type.
 For integers (assuming 2 bytes)
P = P + 1; // Moves P to the next integer (increments address by 2 bytes)
5. Accessing Array Elements via Pointer:
 You can access array elements using the pointer and dereferencing operator
*.

Basic Concepts of One-Dimensional Arrays


Key Points:
1. Array Name Holds Base Address:
o The array name a holds the base address of the array.
2. Array Name is a Constant Pointer:
 Cannot modify the array name to point elsewhere.

3. Subscript Rule:
 Accessing elements using subscript notation can be expressed using pointers.

 The expression *(a + i) calculates the address by adding i * size_of_data_type


to the base address.

4. Equivalent Expressions:
 The following expressions are equivalent in accessing array elements:

 Due to the commutative property of addition.

5. Example:
int value = a[3]; // Accesses the 4th element (index 3)
int value = *(a + 3); // Equivalent to a[3]
int value = *(3 + a); // Also equivalent
int value = 3[a]; // Also equivalent

6. Pointer with Negative Indexing:


 If you set a pointer to a specific element, you can use negative indices to access
previous elements.

 Now, P[0] is a[3], P[-1] is a[2], P[-2] is a[1], etc.


 Example:
Worked Example: Pointer to Array Element

1 Problem Statement:
Write a program that uses a recursive function to process an array of integers.
Code:

Explanation:
1. Array Initialization:
o a[5] = {12, 7, 6, 3, 2};
o Elements at indexes 0 to 4.
2. Function Call:
o F(a, 5); passes the base address of the array and the size 5.
3. Recursive Function F:
o Base Case:
o Even Number Handling:

If the current element *P is even, add it to the result of the recursive call.

o Odd Number Handling:

If the current element *P is odd, subtract the result of the recursive call from
it.

4. Execution Steps:
o Step 1:
1. N = 5, *P = 12 (even)
2. Compute: 12 + F(P + 1, 4)
o Step 2:
1. N = 4, *P = 7 (odd)
2. Compute: 7 - F(P + 1, 3)
o Step 3:
1. N = 3, *P = 6 (even)
2. Compute: 6 + F(P + 1, 2)
o Step 4:
1. N = 2, *P = 3 (odd)
2. Compute: 3 - F(P + 1, 1)
o Step 5:
1. N = 1, *P = 2 (even)
2. Compute: 2 + F(P + 1, 0)
o Step 6:
1. N = 0
2. Base case reached, return 1
5. Calculations:
o Step 5 Result:
1. 2 + 1 = 3
o Step 4 Result:
1. 3 - 3 = 0
o Step 3 Result:
1. 6 + 0 = 6
o Step 2 Result:
1. 7 - 6 = 1
o Step 1 Result:
1. 12 + 1 = 13
6. Final Output:

o The program outputs 13.

4. Quick Evaluation Method (Exam Tip)


To evaluate the recursive function quickly:
 Even numbers: Add the number to the next result.
 Odd numbers: Subtract the next result from the number.
Process:
1. Start with the first element:
o 12 (even): +
2. Next element:
o 7 (odd): -
3. Next element:
o 6 (even): +
4. Next element:
o 3 (odd): -
5. Next element:
o 2 (even): +
6. Base case:
o When N reaches 0, return 1.
Calculation:
 Start from the last element upwards:
o 2+1=3
o 3-3=0
o 6+0=6
o 7-6=1
o 12 + 1 = 13
 Final result: 13

Problem 2: GATE 2016 Question


Program Description
We have a program that defines an array and a recursive function F. The function F
processes the array elements using pointers.
Code
#include <stdio.h>
int F(int *P, int N) {
if (N <= 1)
return 0;
else
return max(F(P + 1, N - 1), P[0] - P[1]);
}
int max(int a, int b) {
return (a > b) ? a : b;
}
int main() {
int a[5] = {3, 5, 2, 6, 4};
printf("%d", F(a, 5));
return 0;
}
Explanation
1. Array Initialization:
o a[5] = {3, 5, 2, 6, 4};
o Elements are stored at consecutive memory locations starting from a base
address (assume base address is 100 for illustration).
o Indexes range from 0 to 4.
2. Function F:
o Prototype: int F(int *P, int N);
o Base Case: If N <= 1, return 0.
o Recursive Case: Return the maximum of:
 F(P + 1, N - 1) (recursive call with next element and decreased size).
 P[0] - P[1] (difference between current and next element).
3. Understanding Pointer Arithmetic:
o P + 1 moves the pointer to the next integer (sizeof(int) bytes ahead).
o P[0] is the current element.
o P[1] is the next element.
Step-by-Step Execution
Let's trace the function calls and evaluate the function F.
Initial Call
 Call: F(a, 5)
 Parameters: P = address of a[0] (100), N = 5
 Condition: N > 1, proceed to recursive call.
Recursive Calls
1. First Level:
o Compute: max(F(P + 1, 4), P[0] - P[1])
o P[0] - P[1]: 3 - 5 = -2
2. Second Level (F(P + 1, 4)):
o P = address of a[1] (102)
o N=4
o Compute: max(F(P + 1, 3), P[0] - P[1])
o P[0] - P[1]: 5 - 2 = 3
3. Third Level (F(P + 1, 3)):
o P = address of a[2] (104)
o N=3
o Compute: max(F(P + 1, 2), P[0] - P[1])
o P[0] - P[1]: 2 - 6 = -4
4. Fourth Level (F(P + 1, 2)):
o P = address of a[3] (106)
o N=2
o Compute: max(F(P + 1, 1), P[0] - P[1])
o P[0] - P[1]: 6 - 4 = 2
5. Base Case (F(P + 1, 1)):
o P = address of a[4] (108)
o N=1
o Return: 0
Backtracking and Computing Max Values
1. Fourth Level Result:
o max(0, 2) = 2
o Return: 2
2. Third Level Result:
o max(2, -4) = 2
o Return: 2
3. Second Level Result:
o max(2, 3) = 3
o Return: 3
4. First Level Result:
o max(3, -2) = 3
o Return: 3
Final Output
 The program outputs: 3

EXTRA NOTE:
The line:

uses the ternary operator to return the maximum of two values, a and b. Here's how it
works:
 Syntax: (condition) ? value_if_true : value_if_false;
 Explanation:
o (a > b): This is the condition being checked.
 If a is greater than b, the condition is true.
o If the condition is true, a is returned.
o If the condition is false (i.e., b is greater than or equal to a), then b is
returned.
This single line is a shorthand way of writing an if-else statement:

Problem 3: GATE 2019 Question


Program Description
This program uses pointer arithmetic and negative indexing to compute a sum based on
array elements.
Code
#include <stdio.h>
int main() {
int a[5] = {2, 4, 6, 8, 10};
int i, sum = 0;
int *B;
B = a + 4;
for (i = 0; i < 5; i++)
sum = sum + (*B - i) - *[B - i];
printf("%d", sum);
return 0;
}

NOTE
B[-i] == *[B - i]

Explanation
1. Array Initialization:
o a[5] = {2, 4, 6, 8, 10};
o Elements stored at indexes 0 to 4.
o Assume base address is 100 (for illustration), with each int occupying 2
bytes.
2. Pointer B:
o Declaration: int *B;
o Initialization: B = a + 4; (Points to a[4] which is 10).
o Memory Address of B: 100 + 4 * 2 = 108
3. Understanding B[-i]:
o Negative indexing from pointer B.
o Indexes relative to B:
 B[0] is a[4] (10)
 B[-1] is a[3] (8)
 B[-2] is a[2] (6)
 B[-3] is a[1] (4)
 B[-4] is a[0] (2)
4. For Loop Execution:
o Loop runs for i from 0 to 4 (total of 5 iterations).
o Loop Body:

Step-by-Step Execution
Let's compute the value of sum for each iteration.
Initial Values
 sum = 0
 B points to a[4] (value 10)

Iteration Details
1. Iteration 1 (i = 0):
o Compute:

sum remains 0
2. Iteration 2 (i = 1):
 Compute:

sum is now 1
3. Iteration 3 (i = 2):
 Compute:
sum is now 3
4. Iteration 4 (i = 3):
 Compute:

sum is now 6
5. Iteration 5 (i = 4):
 Compute:

sum is now 10
Final Output
 The program outputs: 10
Subscript Rule in Arrays
 Subscript Notation:
o a[i] is equivalent to *(a + i).
o Negative indexing: a[-i] is equivalent to *(a - i).
Pointer to One and Two - Dimensional Array

Pointer to an Array Element(RECAP)


Array Declaration

Pointer Declaration

Assignment

Or

Pointer Arithmetic
 When you increment the pointer:

 It moves to the next integer (size of int).

Understanding Two-Dimensional Arrays


Before making a pointer to a one-dimensional array, understand the basics of two-
dimensional arrays.
Two-Dimensional Array Declaration

 a is an array of 3 one-dimensional arrays.


 Each one-dimensional array contains 3 integers.
Memory Representation
 Base Address: Assume 100
 Elements stored in row-major order.
 Each integer occupies 2 bytes (addresses increment by 2).
Accessing Elements
 Subscript Rule:

Pointer Arithmetic Translation of a[i][j]


By definition of pointer arithmetic:
 a[i] is equivalent to *(a + i). This is because:

Here, a + i moves the pointer a to the start of the i-th row.


 Since a[i][j] is accessing the j-th element within the i-th row, we can write:

Substitute a[i] with *(a + i)


Now, we substitute a[i] in *(a[i] + j) using *(a + i):

Pointer to One-Dimensional Array


Pointer Declaration

Assignment

Pointer Arithmetic
 When you increment the pointer:
 It moves to the next one-dimensional array (size of entire one-dimensional array).
 Calculation:
If p points to a[0] at address 100, then after p = p + 1, p points to a[1] at address
106 (assuming int is 2 bytes, so 3 integers × 2 bytes = 6 bytes).

Pointer to a Two-Dimensional Array


Understanding Three-Dimensional Arrays
To make a pointer to a two-dimensional array, understand the basics of three-dimensional
arrays.
Three-Dimensional Array Declaration

 a is an array of 2 two-dimensional arrays.


 Each two-dimensional array contains 3 one-dimensional arrays.
 Each one-dimensional array contains 3 integers.
Memory Representation
 Stored in row-major order.

Pointer to Two-Dimensional Array


Pointer Declaration

Assignment
p = a; // 'p' points to the first two-dimensional array in 'a'

Pointer Arithmetic
 When you increment the pointer:

 It moves to the next two-dimensional array (size of entire two-dimensional array).


 Calculation:
o Each two-dimensional array has 3 × 3 integers.
o Total size per two-dimensional array: 3 * 3 * sizeof(int)
Summary of Pointer Incrementation
Pointer to Element:
o Declaration:

o Incrementation:

Pointer to One-Dimensional Array:


 Declaration:

 Incrementation:

Pointer to Two-Dimensional Array:


 Declaration:

 Incrementation:
p = p + 1; // Moves by N * M * sizeof(int) bytes

Accessing Elements Using Pointers


One-Dimensional Array
 Value at Index i:
 Address of Index i:

Two-Dimensional Array
 Value at a[i][j]:

 Address of a[i][j]:

 Base Address of a[i] (i.e., the ith one-dimensional array):

 Address of a[i][0]:

Important Points
 Subscript Evaluation:
o In C, subscripts are evaluated from left to right.
 Understanding * Operator:
o In a one-dimensional array:
 One * gives the value.
 No * gives the address.
o In a two-dimensional array:
 Two * give the value.
 One * or no * gives the address.
Examples
Example 1: Pointer to One-Dimensional Array
Array Declaration

Pointer Declaration

Assignment

Pointer Arithmetic

Accessing Elements
 Access a[1][2] using pointers:

Example 2: Pointer to Two-Dimensional Array


Array Declaration

Pointer Declaration

Assignment

Pointer Arithmetic
Accessing Elements
 Access a[1][1][2] using pointers:

GATE QUESTION – 2022

Q1
What is the output of the following C code? Assume the base address of the two-
dimensional array X is 2000, and each int occupies 4 bytes of memory.

Answer:
The output of the program will be three integer addresses, and all three addresses will be
2036.
Explanation:
Let's break down the code and understand why each expression evaluates to 2036.
Given:
 Base address of X (&X[0][0]) is 2000.
 Each int occupies 4 bytes.
 X is declared as int X[4][3];, which means:
o There are 4 rows (0 to 3).
o Each row has 3 columns (0 to 2).
Memory Layout:
The two-dimensional array X can be visualized as:

Evaluating the Expressions:


1. X + 3
o X is the base address (2000).
o X is of type int (*)[3] (pointer to an array of 3 integers).
o When we do X + 3, we move 3 blocks ahead, where each block is the size of
int[3].
o Size of each block: 3 integers × 4 bytes = 12 bytes.
o Address calculation:

2. *(X + 3)
 X + 3 points to X[3], as calculated above.
 *(X + 3) dereferences it.
 Since X[3] is an array, it decays to a pointer to its first element X[3][0].
 Therefore, *(X + 3) gives the address of X[3][0], which is 2036.
3. *(X + 2) + 3
 X + 2 points to X[2].
 *(X + 2) dereferences it to get the array X[2].
 X[2] decays to a pointer to its first element X[2][0] (address 2024).
 The type of *(X + 2) is int *.
 Adding 3 increments the pointer by 3 integers:

o So, *(X + 2) + 3 points to the address 2036.


Printing the Addresses:

The printf statement is:


 The printf statement is:

 All three expressions evaluate to the address 2036.


 Therefore, the output will be:

Note: Since we're printing addresses using %d, we're assuming that pointer values
can fit into an int. In practice, it's better to use %p and cast pointers to (void *)
when printing addresses to ensure portability and correctness.
Q2
Context:
We have the following C code involving variables, arrays, and pointers:

Question:
What is the output of the program?

Answer:
The output of the program is:

Memory Representation:
Let's assume the following memory addresses (for illustration):
 Addresses:
o x: Address 100
o z[0]: Address 200
o z[1]: Address 204 (assuming int is 4 bytes)

Breakdown of the expression:

 The pointer p now holds the address of x (100).


 p points to x.

 The value at the address pointed to by p is updated to 10.


 Since p points to x, x is updated from 1 to 10.

  The pointer p now holds the address of z[1] (204).


 p points to z[1].

  &z[0]: Address of z[0] (200).


 &z[0] + 1: Address of z[0] plus one int size (points to z[1]).
o Since int is 4 bytes, &z[0] + 1 equals 200 + 1 * 4 = 204.
 *(&z[0] + 1): Dereferences the address to get the value at z[1] (currently 11).
 *(&z[0] + 1) += 3: Increments the value at z[1] by 3.
 z[1] is updated from 11 to 14.
 Prints the values of x, z[0], and z[1].
 Values:
 x: 10 (updated in Step 2)
 z[0]: 10 (unchanged)

 z[1]: 14
Array of Pointers (q3 to q5 after this)

Introduction
 Concept: Each location in an array can hold an address, making every location a
pointer.
 Use Case: An array of pointers is useful when multiple variables' addresses need to
be stored in multiple pointers.

Declaration:

Syntax Breakdown:
o int *: Pointer to an integer.
o p[3]: Array of size 3.

Assigning Addresses to Array Elements:

Memory Representation:
Accessing Values Using Array of Pointers
Using Dereferencing:

Using a Loop:

o Explanation:
 The loop iterates over the array indices 0 to 2.
 *p[i] accesses the value pointed to by p[i].

Understanding the Declaration


 Operator Precedence in C:
o Highest Priority:
 [] (Array subscript)
 () (Function call)
 -> (Structure pointer)
 . (Structure member)
o Next Priority:
 Unary operators: * (Indirection), & (Address), +, -, !, etc.
 Interpreting int *p[3];:
o Due to operator precedence, [] binds before *.
o Reading the Declaration:
 p is an array of 3 pointers to integers.
 Each p[i] is a pointer to an int.
Detailed Evaluation of *p[i]
 Subscript Rule:
o In C, a[i] is equivalent to *(a + i).
 Evaluation Steps for *p[i]:
1. When i = 0:
 *p[0] becomes *(*(p + 0)).
 First, p + 0 gives the address of p[0].
 Second, *(p + 0) dereferences to get the pointer stored at p[0]
(which is &a or address 100).
 Third, *(*(p + 0)) dereferences again to get the value at address 100,
which is 20.

Memory Address Calculations:


 Size of Pointer: Assume 2 bytes (this can vary based on system architecture).
 Address Calculation:

Complete Program Example


#include <stdio.h>
int main() {
int a = 20, b = 30, c = 40;
int *p[3]; // Array of 3 integer pointers
int i;
// Assign addresses to array of pointers
p[0] = &a;
p[1] = &b;
p[2] = &c;

// Access values using a loop


for (i = 0; i < 3; i++) {
printf("%d\n", *p[i]); // Outputs 20, 30, 40
}
return 0;
}

Output:
20
30
40
Q3
We have the following C code involving a two-dimensional array and pointer arithmetic:

Answer:
The output of the program is:

Array Declaration:

 arr is a 2D array with 5 rows and 5 columns.


 However, we only initialize arr[0..3][0..4] due to the loop conditions.

Loop Conditions:

 The outer loop runs for i = 0, 1, 2, 3 (4 iterations).


 The inner loop runs for j = 0, 1, 2, 3, 4 (5 iterations).
 Total elements initialized: 4 rows * 5 columns = 20 elements.

Initializing Elements:
We initialize each element with:

Now, let's fill the array based on the loop conditions.

Note: The row i = 4 is not initialized because i < 4

Explanation of the Pointer Expression


The program uses this pointer expression:

To understand it:
1. arr + 1: Points to the second row (arr[1]).
2. *(arr + 1): Dereferences to arr[1], which is the starting address of the second row.
3. *(arr + 1) + 9: Moves 9 positions forward in the flat memory representation, which
corresponds to arr[2][4].
4. *(*(arr + 1) + 9): Dereferences to retrieve the value at arr[2][4].
Value at arr[2][4]
From the matrix, arr[2][4] is 24. Thus, the output of the program is:
Q4
Question:
We have the following scenario involving a two-dimensional array and pointer arithmetic:
 An array A is a two-dimensional array consisting of four one-dimensional arrays.
 Each one-dimensional array contains five elements.
 The elements of the array are as follows:

 Assume the base address of the array A is 100.


 We are asked to evaluate the following expression:

What is the value of this expression?

Answer:
The value of the expression *(*(A + 3) + 3) is 19.
Q5
Question:
Consider the following scenario involving a three-dimensional array:
 An array A is defined as A[3][3][3], which means:
o There are 3 two-dimensional arrays.
o Each two-dimensional array contains 3 one-dimensional arrays (rows).
o Each one-dimensional array contains 3 elements (columns).
 The array is initialized with values from 1 to 27 in sequence.
 We have the following code snippet:
#include <stdio.h>
int main() {
int A[3][3][3];
int i, j = 0, k;
// Initializing the array
int value = 1;
for (int x = 0; x < 3; x++)
for (int y = 0; y < 3; y++)
for (int z = 0; z < 3; z++)
A[x][y][z] = value++;
// Printing specific elements
for (i = 0; i < 3; i++) {
for (k = 0; k < 3; k++) {
printf("%d ", A[i][j][k]);
}
}
return 0;
}
1. Understanding the Array Structure:
 A[3][3][3] creates a 3D array.
 Indices for the dimensions:
o i (first dimension): 0 to 2 (Two-dimensional array index)
o j (second dimension): 0 to 2 (Row index within a 2D array)
o k (third dimension): 0 to 2 (Column index within a row)
 The array is filled with values from 1 to 27.
2. Initializing the Array:
The nested loops fill the array sequentially:
 A[0][0][0] = 1, A[0][0][1] = 2, ..., A[2][2][2] = 27
3. Visual Representation of the Array:
First Two-dimensional Array (i = 0):

Second Two-dimensional Array (i = 1):

Third Two-dimensional Array (i = 2):

4. Analyzing the Printing Loop:

 j is initialized to 0 and remains constant.


 The outer loop iterates over i from 0 to 2.
 The inner loop iterates over k from 0 to 2.
Elements Being Printed:
We are printing A[i][0][k] for i = 0 to 2 and k = 0 to 2.

When i = 0:
 A[0][0][0] = 1
 A[0][0][1] = 2
 A[0][0][2] = 3
When i = 1:
 A[1][0][0] = 10
 A[1][0][1] = 11
 A[1][0][2] = 12
When i = 2:
 A[2][0][0] = 19
 A[2][0][1] = 20
 A[2][0][2] = 21

5. Compiling the Output:


The program prints the elements in the order:
Array of Pointers Questions
Question 1:
We have the following C program that demonstrates how to access values using an array
of pointers and pointer arithmetic:
#include <stdio.h>
int main() {
int a[5] = {10, 20, 30, 40, 50};
int *p[5];
int **ptr;
// Initializing the array of pointers
p[0] = a; // Points to a[0]
p[1] = a + 3; // Points to a[3]
p[2] = a + 4; // Points to a[4]
p[3] = a + 1; // Points to a[1]
p[4] = a + 2; // Points to a[2]
// Creating a pointer to the array of pointers
ptr = p;
// Performing pointer arithmetic
ptr++; // Moves ptr to point to p[1]
// Printing the results
printf("%d %d\n", ptr - p, **ptr);
return 0;
}
Question:
What is the output of the program?
Answer:
The output of the program is:
Detailed Explanation:
Array of Pointers p
 Elements and Stored Addresses:

Assuming base address 200 for p and each pointer occupies 2 bytes.

Pointer ptr
 Value and Points To:
o ptr holds the address of p, which is 200.
o ptr is of type int **.

Pointer Arithmetic and Operations


1. Incrementing ptr
 Operation:

 Explanation:
o Initially, ptr points to p[0] (address 200).
o After ptr++, it points to p[1] (address 202).
o Since ptr is a pointer to a pointer (int **), incrementing it moves it to the
next pointer-sized memory location.

2. Printing Values Using ptr


 Statement:

Calculations:
 ptr - p:
o (ptr - p) calculates the number of elements between ptr and p.
o Addresses:
 ptr is now at 202.
 p is at 200.
o Pointer Difference:

o Output: 1

 **ptr:
o *ptr dereferences ptr to get the value at ptr, which is p[1] (address 106).
o **ptr dereferences again to get the value at address 106, which is 40 (a[3]).
o Output: 40

Additional Example: Difference Between Two Pointers


1. Setup
 Declare an Array:
 Declare Two Pointers:

2. Calculating the Difference


 Addresses:
o p1 holds address 100.
o p2 holds address 108.
 Pointer Difference:

Calculation:
 Difference in addresses: 108 - 100 = 8.
 Size of int: 2 bytes.
 Number of elements between p1 and p2:

Explanation:
 There are 4 elements between p1 and p2.
 This means p2 is 4 elements ahead of p1 in the array.

Printing the Difference

Invalid Pointer Operations


 Operations Not Allowed:
o Addition of Two Pointers:

o Multiplication of Two Pointers:


o Division of Two Pointers:

o Modulus of Two Pointers:

Reason:
 Pointers represent memory addresses.
 Arithmetic operations like addition, multiplication, division, and modulus between
two pointers are not meaningful and thus not allowed.

Additional Notes
 Size of Data Types:
o The size of int and pointers (int *) can vary depending on the system
architecture (e.g., 2 bytes, 4 bytes, or 8 bytes).
o For consistent results, use sizeof(int) and sizeof(int *) in your calculations.

General Rule in C
The behavior of p and ptr follows these conventions:
 For an array name like p:
o When used in an expression, p decays into a pointer to its first element
(&p[0]).
 For a pointer like ptr:
o The value of ptr is used unless explicitly dereferenced or its address is
explicitly taken (e.g., &ptr).

How [] and * Work Together


 p[i]: Accesses the i-th element of p (the pointer stored in p[i]).
 *p[i]: Dereferences the pointer stored in p[i] to get the value in the a[] array.
 *p: Is shorthand for p[0], which gives you the first pointer in the array.
 **p: Dereferences p[0] to access the value in the a[] array that p[0] points to.

Question 2:
We have the following C program involving global arrays, an array of pointers, and pointer
arithmetic:

#include <stdio.h>
// Global arrays
int a1[] = {6, 7, 8, 18, 34, 67};
int a2[] = {23, 56, 28, 29};
int a3[] = {-12, 27, -31};
// Array of pointers
int *x[3] = {a1, a2, a3};
// Function declaration
void print(int *a[]);
int main() {
print(x);
return 0;
}
// Function definition
void print(int *a[]) {
printf("%d ", a[0][2]);
printf("%d ", *a[2]);
printf("%d ", *++a[0]);
printf("%d ", *(++a)[0]);
printf("%d\n", a[-1][1]);
}
What is the output of the program, and how is it computed step by step?
Answer:
The output of the program is:
Visualization:

Why Use Alias a Instead of x Directly?


 Array Names are Constant Pointers:
o The name x is a constant pointer; it cannot be incremented or
decremented.
o Operations like ++x are invalid because you cannot modify a constant
pointer.
 Using Alias a:
o By passing x to the function and accepting it as a parameter a, we can
manipulate a without restrictions.
o Inside the function, a is a local variable that points to x.
o We can perform pointer arithmetic on a.

Step-by-Step Execution of Print Statements


Let's evaluate each printf statement one by one.
Initial Setup
At the start of the print function:
 a is a copy of x.
 a[0] points to a1[0] (value 6).
 a[1] points to a2[0] (value 23).
 a[2] points to a3[0] (value -12).

1. printf("%d ", a[0][2]);


 Explanation:
o a[0] points to a1[0] (start of a1).
o a[0][2] accesses the element at index 2 of a1.
o So, a[0][2] is a1[2].
 Calculation:
o a1[2] is 8.

 Output:

2. printf("%d ", *a[2]);


 Explanation:
o a[2] points to a3[0] (start of a3).
o *a[2] dereferences a[2] to get the value at a3[0].
 Calculation:
o *a[2] is -12.
 Output:

3. printf("%d ", *++a[0]);


 Explanation:
o ++a[0] increments the pointer a[0].
o Originally, a[0] points to a1[0] (value 6).
o After ++a[0], it points to a1[1] (value 7).
o *a[0] dereferences the incremented pointer to get the value.
 Calculation:
o *a[0] is 7.
 State Update:
o a[0] now points to a1[1].
 Output:

4. printf("%d ", *(++a)[0]);


 Explanation:
o ++a increments the pointer a, which is an array of pointers.
o Originally, a points to a[0] (which points to a1[1] after previous increment).
o After ++a, a points to a[1] (which points to a2[0]).
o *(a)[0] accesses the pointer at a[0] and dereferences it to get the value.
 Calculation:
o a[0] now points to a2[0] (value 23).
o *(a)[0] is 23.
 State Update:
o a now points to the second element of the original array (x[1]).
 Output:

5. printf("%d\n", a[-1][1]);
 Explanation:
o a currently points to a[1] (due to previous ++a).
o a[-1] refers to the element before the current a, which is a[0] (after
modification).
o a[-1] points to a1[1] (value 7), because a[0] was incremented earlier.
o a[-1][1] accesses the element at index 1 of the array pointed to by a[-1].
 Calculation:
o a[-1] points to a1[1] (value 7).
o The array starting from a1[1] is {7, 8, 18, 34, 67}. ( 7 is 0 index in this case
thus 1 index becomes 8
o a[-1][1] is the second element of this array: a1[1 + 1] or a1[2].
o a1[2] is 8.
 Output:

Final Output
Combining all the outputs from the print statements:

Pointer to String Basics


String Constant
Definition: A string constant is a group of characters enclosed in double quotes.
 Example: "apple" is a string constant.
 More Examples:
o "12345" (contains numeric characters)
o "EBC123" (alphanumeric)
o "A" (even a single character in double quotes is a string constant)
o Anything enclosed in double quotes is considered a string constant.
Key Point:
A string constant is defined as:
 "Group of characters enclosed in double quotes"

Characteristics of a String Constant


1. Every Character as a Character Constant:
Every character in a string constant can be treated as a character constant. They
are stored in memory sequentially in contiguous memory locations.
2. Example: "India"
Let’s consider the string constant "India":
o User’s point of view: Each character (I, n, d, i, a) is stored as a character
constant in consecutive memory locations.
o Suppose the starting address is 100.
Memory layout (user perception):

1. Null Termination: Every string constant is automatically terminated with a null


character '\0'.
Size vs. Length:
o Size includes the null character.
o Length counts only the actual characters, not the null terminator.
For "India":
o Characters: I (1), n (2), d (3), i (4), a (5)
o Null terminator: \0 at the end
o Size = 6 bytes (including null character)
o Length = 5 (not counting the null character)
2. Compiler’s Internal Representation (ASCII Values):
Although we think in terms of characters, the compiler converts these character
constants into their corresponding ASCII values. Eventually, these ASCII values
might be stored in binary form.

Importance of ASCII Values


GATE has asked questions based on ASCII values. Sometimes a table is given, sometimes
not. It’s crucial to remember at least the ASCII values of alphabets, or understand how to
derive them.
 ASCII value of 'A': 65
 Knowing 'A' = 65, we can find other letters by counting forward.
Examples:
 'I': 'I' is the 9th letter of the alphabet.
Calculation:
o 'A' = 65
o 'I' is the 9th letter (A=1, B=2, ..., I=9)
o ASCII('I') = 65 + (9 - 1) = 65 + 8 = 73.
 'H': 'H' is the 8th letter.
ASCII('H') = 65 + (8 - 1) = 72.
 'T': 'T' is the 20th letter.
ASCII('T') = 65 + (20 - 1) = 84.
String Constants in Memory
 String constants are stored in static memory.
 Every string constant ends with a null character '\0'.
Example:

 The base address of "India" is returned and assigned to p.


 p points to 'I'.
 *p prints 'I'.
The static memory layout might look like:

Difference Between String Constant and String Variable Initialization


Consider another program snippet:
int main() {
char C[9] = "GATE2023";
char *P = C;
printf("%s", P);
// If we do operations like P + (something), we must consider ASCII values...
}
Analysis:
 Here C is declared as char C[9], and initialized with "GATE2023".
 This is a string variable (an array), not just a pointer to a string constant.
 Because C is an array (a string variable), it will be stored on the stack at runtime,
not in static memory.
 C has enough space for 'G', 'A', 'T', 'E', '2', '0', '2', '3', and '\0'.
Memory layout (stack at runtime):

P is a pointer to char, stored on the stack as a local variable. P = C; means P holds the base
address of C (e.g., 100).
Important Note About %s in printf:
 %s expects an address of a string.
 It prints characters starting from that address until it encounters a null character '\
0'.
If you do arithmetic like P + P[3] - P[1], consider ASCII values:
 P of 3: E, ASCII value = 69.
 P of 1: A, ASCII value = 65.
When adding a constant to an address, the constant is multiplied by the size of the data
stored at the address.
 For a character (size = 1 byte), the constant is multiplied by 1.
 For an integer (size = 4 bytes), the constant is multiplied by 4. (IT DEPENDS)
Difference: 69 X 1 - 65 X 1 = 4.
Address update: Starting address (100) + 4 = 104.

Result
 The address 104 is used.
 The program starts printing characters stored at address 104 character by
character until it encounters a null character.
 The output printed from this process is 2023.

Question 1
void abc(char *s) {
if (s[0] == '\0') {
return; // If first character is null terminator, stop recursion.
}
abc(s + 1); // First recursive call
abc(s + 1); // Second recursive call
printf("%c", s[0]); // Print the first character after both recursive calls return
}
If main() calls abc("123");, what will be the output of the program? Also, how does the
recursion proceed step-by-step, and what is the pattern for the number of function calls
and characters printed if the string length is N?
Answer

Given: The input string is "123", which in memory looks like this:

The base address of the string constant "123" is (for example) 100.
To Do:
1. Trace the execution of abc("123").
2. Determine the output printed by the program.
3. Count the total number of function calls.
4. Generalize the number of function calls and characters printed for a string of
length N.

Step-by-Step Execution:
Passing "123" to abc means abc receives a base address (say 100).
1. Initial Call: abc(100) with s[0] = '1'. Not null, so:
o Call abc(101)
o Call abc(101) again
o Then printf("%c", '1')
2. First abc(101): s[0] = '2'. Not null, so:
o Call abc(102)
o Call abc(102) again
o Then printf("%c", '2')
3. abc(102): s[0] = '3'. Not null, so:
o Call abc(103)
o Call abc(103) again
o Then printf("%c", '3')
4. abc(103): s[0] = '\0'. Null character found, return immediately. No print.

Unwinding the Recursion:


 When abc(102) calls abc(103) twice, both return immediately (no print). Then
abc(102) prints '3'.
Thus, one abc(102) call results in printing: 3
 abc(102) is called twice by abc(101), so together:
o First abc(102) -> prints 3
o Second abc(102) -> prints another 3
o After both return, abc(101) prints 2
So abc(101) results in: 3 3 2
 abc(101) is also called twice by abc(100):
o First abc(101) -> 3 3 2
o Second abc(101) -> 3 3 2
o After both, abc(100) prints 1
Final output from abc(100): 3 3 2 3 3 2 1
Counting Function Calls:
 Let’s count how many total calls happen for the input "123":
o At abc(100), there is 1 initial call.
o It calls abc(101) twice -> 2 calls
o Each abc(101) calls abc(102) twice -> 2 * 2 = 4 calls
o Each abc(102) calls abc(103) twice -> 4 * 2 = 8 calls
Total = 1 (for abc(100)) + 2 (for abc(101)s) + 4 (for abc(102)s) + 8 (for abc(103)s) = 15 calls.
Number of Characters Printed:
Every function call generates 1printf
 Printed characters are: 3 3 2 3 3 2 1
 Total 7 characters printed.

Recursive Function Analysis


Number of function calls:
For N = 3:
 Start with 1 call, then 2 calls, then 4 calls, and so on. The total number of
function calls is: 1+2+4+8 which can be written as 1+2^1 + 2^2 + 2^3
Thus For N = n we can write
 1+2^1 + 2^2 + ……….2^n
Mathematical Derivation:
1. The sum of a geometric progression with N+1 terms is:

Here:

 a=1a = 1a=1 (the first term),


 r=2r = 2r=2 (common ratio).

Substituting these:
Characters Printed:
o Characters are printed corresponding to each function call in a specific
pattern. For N=3N = 3N=3, the total characters printed are: 1+2+4=7 which
can be written as 1 + 2^1 + 2^2 or 3-1(for N = 3)
o For N = N it becomes 1 + 2^1+ 2^2……….2^N-1

o  The terms go from with N terms.


o  The sum is

(2^N – 1 + 1 = 2^N)
Formulas for a General String Length N:
 Let the string length = N (not counting the null terminator).
 Number of function calls: 2^(N+1) - 1
 Number of characters printed: 2^N - 1
Verification for N=3:
 Function Calls: 2^(3+1)-1 = 2^4 -1 =16 -1=15
 Characters Printed: 2^3 -1 =8 -1=7
FUNCTION TREE(PRINTF NOT DISCRIBED)
Problem 2: Pointer-to-String Concept with Spaces
Question: Now consider another function foo that takes a string and recursively processes
it. Suppose the input string is "ABCD EFGH" (with a space between 'D' and 'E') and is
terminated by '\0'.
Let’s assume foo operates as follows:

Given String: "A B C D E F G H \0" (Spaces represented for clarity)


In memory (example):

Here, a starts at address 100 pointing to 'A'.


Execution Trace (Emphasizing the Unwinding Phase):
1. foo(100):
o *a = 'A'. Not '\0' and not a space. Condition true.
o Calls foo(101).
o After foo(101) returns, it will print 'A'.
2. foo(101):
o *a = 'B'. Condition true.
o Calls foo(102).
o After foo(102) returns, it will print 'B'.
3. foo(102):
o *a = 'C'. Condition true.
o Calls foo(103).
o After foo(103) returns, it will print 'C'.
4. foo(103):
o *a = 'D'. Condition true.
o Calls foo(104).
o After foo(104) returns, it will print 'D'.
5. foo(104):
o *a = ' '. Space encountered.
o Condition fails. No recursive call, no print.
o foo(104) returns immediately.
Unwinding (When Printing Actually Occurs):
 Return to foo(103):
o Now the call foo(104) has completed and returned.
o foo(103) executes putchar('D').
o Outputs: D
 Return to foo(102):
o foo(102) now prints putchar('C').
o Outputs so far: DC
 Return to foo(101):
o foo(101) now prints putchar('B').
o Outputs so far: DCB
 Return to foo(100):
o foo(100) now prints putchar('A').

o Final outputs: DCBA

FUNCTION TREE(PRINTF NOT DISCRIBED)


Problem 3: Using strlen with Computed Addresses
what is the output of printf("%d", (int)strlen(C + (P[2] - P[6] + 1)));?

Explanation of the Code:


1. String Definition:
o char *C = "GATECSIT2023"; defines the constant string.
o P is set to the same address as C.
2. Offset Calculation:
o (P[2] - P[6] + 1) is calculated based on the ASCII values of characters in the
string:
 P[2] → 'T' (ASCII = 84)
 P[6] → 'I' (ASCII = 73)
 Offset = (84 - 73 + 1) = 12
3. strlen:
o C + offset points 12 characters ahead of the start of the string.
o strlen(C + 12) calculates the length of the string starting at that point.

o Since it points past the null terminator, strlen returns 0.

o Extra – strlen, does not include the null terminator itself in the count.
So, strlen() counts starting from the current position of the string and stops
at the null terminator.

Problem 4: Unsigned Integer Comparison in strlen Results


#include <stdio.h>
#include <string.h>
int main() {
unsigned int C = 0;
char *S = "abc";
char *T = "defgh";
// (-2 > 0) is true due to unsigned comparison
int LEN = ((strlen(S) - strlen(T)) > C) ? (int)strlen(S) : (int)strlen(T);
printf("%d\n", LEN); // 3

return 0;
}
Key Point:
Unsigned comparisons can cause counterintuitive outcomes when negative signed values
are involved.
What will LEN be?
Detailed Reasoning:
 strlen(S) = 3
 strlen(T) = 5
 (strlen(S)-strlen(T)) = 3 - 5 = -2
Comparing -2 (a signed negative integer) with C (an unsigned integer 0):
 When compared to an unsigned value, -2 is converted to a very large unsigned
number.
 Thus, -2 > 0 is evaluated as true.
 Hence, LEN = strlen(S) = 3.
Answer:
LEN becomes 3.
Problem 5: Swapping String Pointers Using Single and Double Pointers
#include <stdio.h>
void F1(char *STR1, char *STR2) {
char *T = STR1;
STR1 = STR2;
STR2 = T;
}
void F2(char **STR1, char **STR2) {
char *T = *STR1;
*STR1 = *STR2;
*STR2 = T;
}
int main() {
char *S1 = "hi";
char *S2 = "bye";
F1(S1, S2);
printf("%s %s\n", S1, S2); // hi bye
F2(&S1, &S2);
printf("%s %s\n", S1, S2); // bye hi
return 0;
}

 F1 tries to swap the pointers by passing them directly.


 F2 tries to swap the pointers using their addresses (double pointers).

Detailed Reasoning:
1. Function F1 (Pass by Value):
After F1(S1, S2), S1 and S2 remain "hi" and "bye" in main().
Printing now shows: hi bye.
2. Function F2 (Pass by Reference):
1. After F2(&S1, &S2), pointers are actually swapped in main().
Now S1 = "bye" and S2 = "hi".
Printing now shows: bye hi.

Answer:
 After F1(S1, S2): hi bye
 After F2(&S1, &S2): bye hi

 Memory Layout for the Pointer Swapping Problem


Before calling F2:

After F2(&S1, &S2), the stack pointers S1 and S2 are swapped:

Problem 6: Complex Pointer-to-String Examples and Arithmetic


Perform complex arithmetic such as **(++P) + 3, *--(*++P) + 1, and print results.
#include <stdio.h>

int main() {
// Four string constants stored in static memory
const char *S[4] = {"ice", "green", "cone", "please"};

// Another array of pointers (each holding addresses of S's elements)


// Suppose STR[0] = S+3, STR[1] = S+2, STR[2] = S+1, STR[3] = S
// Meaning STR points to pointers within S array.
const char **STR[4] = { S+3, S+2, S+1, S };
// P is a pointer to a pointer to a pointer to char
const char ***P = STR;

return 0;
}

Memory Address | Content


---------------|------------------------------------------------
100 | 'i' (part of "ice")
101 | 'c' (part of "ice")
102 | 'e' (part of "ice")
103 | '\0' (null terminator for "ice")
---------------|------------------------------------------------
200 | 'g' (part of "green")
201 | 'r' (part of "green")
202 | 'e' (part of "green")
203 | 'e' (part of "green")
204 | 'n' (part of "green")
205 | '\0' (null terminator for "green")
---------------|------------------------------------------------
300 | 'c' (part of "cone")
301 | 'o' (part of "cone")
302 | 'n' (part of "cone")
303 | 'e' (part of "cone")
304 | '\0' (null terminator for "cone")
---------------|------------------------------------------------
400 | 'p' (part of "please")
401 | 'l' (part of "please")
402 | 'e' (part of "please")
403 | 'a' (part of "please")
404 | 's' (part of "please")
405 | 'e' (part of "please")
406 | '\0' (null terminator for "please")
---------------|------------------------------------------------
500 | 100 (S[0] pointing to "ice")
502 | 200 (S[1] pointing to "green")
504 | 300 (S[2] pointing to "cone")
506 | 400 (S[3] pointing to "please")
---------------|------------------------------------------------
600 | 506 (STR[0] pointing to S[3])
602 | 504 (STR[1] pointing to S[2])
604 | 502 (STR[2] pointing to S[1])
606 | 500 (STR[3] pointing to S[0])
---------------|------------------------------------------------
700 | 600 (P pointing to STR[0])

Example 1

Step-by-step:
1. ++P increments P. Originally P = &STR[0], after ++P, P = &STR[1] (now P points to
602).
2. *P = STR[1] = (S+2), which points to S[2].
3. **P = *(S+2) = S[2] = "cone".
4. **P + 3 = "cone" + 3 = pointer to the character 'e' in "cone".

So this prints "e".

Example 2
Now P is at STR[1]. Consider:
1. P still points to STR[1].
2. *P = STR[1] = S+2
3. **P = *(S+2) = S[2] = "cone"
So this prints "cone".

Example 3
P = STR; // P points to STR[0]
++P; // P now points to STR[1], which corresponds to "cone".
printf("%s", **P + 4);
**P = "cone". "cone" + 4 points to the null terminator '\0'. Printing from '\0' prints
nothing.

Example 4
Expression: *--(*++P) + 1
 ++P increments P to point to STR[1].
 *++P gives STR[1], which points to S+2.
 --(*++P) decrements the pointer S+2 to S+1, pointing to "green".
 *--(*++P) dereferences to get "green".
 Adding 1 to "green" advances the pointer by one character to "reen" (since
"green" is a string, +1 moves the pointer past the first character)
Extra -
1. Pointer Size is Independent of Data Size:
o A pointer stores an address in memory, so its size depends on the total
memory addressable by the system, not on the type of the variable it points
to.
2. System Architecture and Pointer Size:
o In a 16-bit system, pointers are typically 2 bytes.
o In a 32-bit system, pointers are 4 bytes.
o In a 64-bit system, pointers are 8 bytes.
3. Data Types and Sizes:
o The char type requires only 1 byte since it is used to store individual
characters, which are 8 bits wide in most systems
Structures
Basics of Structures
 Structure Declaration:
o Example:

o At this point, no physical memory is allocated; it only informs the compiler


that Student is a user-defined data type.
Structure Variable Declaration:
 Example:

s1 is a variable of type Student.


Structure Variable Initialization:
 Example:

Allocates memory equivalent to the size of the structure:


 Integer: 2 bytes
 Character array (5 bytes): Total 7 bytes.
Array of Structures
 Declaring an array of structures:

Explanation:
 Creates an array a of three Student structures.
 Each structure contains:
o idno (integer).
o s (character array).

Pointer to Structure Array:


 Example

Accessing Members:
 Using array:

 a[i].idno; - Accesses the idno member of the i-th struct in the


array a.
 a[i].s; - Accesses the s member of the i-th struct in the array a.
 Using pointer:

 (p+1)->idno - Moves the pointer to the next struct in the array and accesses its idno
member (equivalent to a[1].idno).
 (p+2)->s - Moves the pointer two structs over from the start and accesses the s
member (equivalent to a[2].s).
 p + 1 moves to the next structure by its size (e.g., 7 bytes).
Nesting of Structures
 Example:

Memory Layout:
 idno: 10
 dob: {26, 6, 1983}
 s: "Sai"
Accessing Nested Structure Members:
Inline Declaration:

Notes
 Invalid Initialization: Structure members cannot be initialized directly; they must
be initialized through variables.
 Pointer Access: Use the -> operator or dereference (*) with dot (.) operator for
accessing members.
Problems
Write a program that involves nested structures and demonstrates the use of pointers and
character arrays.
Code:

Solution:
1. Declaration of Structures:
o struct a has:
 char ch[7] → Character array.
 char *str → Pointer to a string.
o struct b has:
 char *c → Pointer to a string.
 struct a ss1 → Nested structure of type struct a.
2. Initialization:
o S2.c → Points to "Raipur".
o S2.ss1.ch → Holds "Kanpur".
o S2.ss1.str → Points to "Jaipur".
3. Memory Layout:

4. Access Mechanism:
o printf("%s %s", S2.c, S2.ss1.str):
 Prints "Raipur" and "Jaipur".
o ++S2.c:
 Moves pointer to the next character of "Raipur".
 Now points to "aipur".
o ++S2.ss1.str:
 Moves pointer to the next character of "Jaipur".
 Now points to "aipur".

Output:

Diagram:
Initial State:

Static Memory

"Raipur" → Base Address: 100

"Jaipur" → Base Address: 200

S2 Layout:
Updated Layout:

Write a program that demonstrates self-referential structures using an array


of structures.
Code:

Solution:
1. Structure Declaration:
o struct sl contains:
 char *z → String pointer.
 int i → Integer.
 struct sl *p → Pointer to a structure of the same type.
2. Initialization:
o a[0]: z = "Nagpur", i = 1, p = a+1 (points to a[1]).
o a[1]: z = "Raipur", i = 2, p = a+2 (points to a[2]).
o a[2]: z = "Kanpur", i = 3, p = a (points to a[0]).
3. Access Mechanism:
o a[0].z → "Nagpur".
o ptr->z → "Nagpur" (ptr points to a[0]).
o a[2].p->z → "Nagpur" (p points back to a[0]).

Output:

Diagram:
Memory Layout:

Pointer Representation:

Understanding the Expressions:


1. a[0].z:
o a[0] refers to the first element of the array a.
o .z accesses the z member of the struct sl at a[0].
o a[0].z points to "Nagpur".
Value: "Nagpur"

2. ptr->z:
o ptr is initialized as:

o This means ptr points to the base address of the array a, i.e., a[0].
o ptr->z is equivalent to (*ptr).z, which means dereference ptr and access the
z member.
o Since ptr points to a[0], ptr->z gives the same result as a[0].z.
Value: "Nagpur"

3. a[2].p->z:
o a[2].p: This points to a[0] (because of the initialization a[2].p = a).
o a[2].p->z:
 a[2].p points to a[0].
 .z accesses the z member of a[0].
 So, a[2].p->z is the same as a[0].z.
Value: "Nagpur"

Diagram:
Problem 1 (GATE 2018 – Struct Pointer Arithmetic):
Problem:

Options:
a) 0, c
b) 0, a + 2
c) '0', 'a + 2'
d) '0', 'c'

Explanation:
1. Structure Definition and Initialization:
o struct Ournode has 3 char members (x, y, z).
o p is initialized as:

2. Pointer Initialization:

q points to the base address of p.

3. Pointer Arithmetic:
 (char*)q casts q to a char* to allow byte-wise increments.
 ((char*)q + 1) points to p.y (address 101).
 ((char*)q + 2) points to p.z (address 102).

4. Dereferencing and Output:


 *((char*)q + 1) → p.y = '0'
 *((char*)q + 2) → p.z = 'c'

Answer:

Problem 2 (GATE 2015 – Character Pointer and Array Modification):


Problem:

Options:
a) 1204567
b) 1234567
c) 1204567 (in single quotes)
d) 1200000

Explanation:
1. Memory Allocation and Initialization:

 s1 is a character array storing the string "1234567".


2. Pointer Arithmetic:

 p points to the 3rd element (index 2) → s1[2] = '3'

3. Modification:

 s1[2] is modified to '0'.

4. Final Memory Layout:


5. Output:
printf("%s", s1); prints the string starting from the base address of s1.

Problem 3 (String Length and Loop – GATE 2004):


Problem:

Options:
a) STRING
b) GNIRTS
c) Null output
d) No output

Explanation:
1. Initialization:

2. Loop Behaviour:
 The loop copies characters from the end of the string to the start of p[].
 Important: s[length] is \0 (null character), causing the first element of p[] to be
null.
3. Final Memory Layout:

4. Output:
 printf("%s", p); starts at p[0] (null character), so no output is printed.

Answer:

Problem 4:

Explanation:

Step 1: ASCII Values of Characters


 'P' → ASCII 80
 'x' → ASCII 120
 '*' → ASCII 42
 '-' → ASCII 45
 '+' → ASCII 43

Step 2: Expression Breakdown (c, d, e)


1. char c = (a & b) + '*';
Bitwise AND (&) between a and b:

 (a & b) = 80 (ASCII 'P').


 80 + 42 ('*') = 122 → ASCII 122 = 'z'.

2. char d = (a | b) - '-';
Bitwise OR (|) between a and b:

 (a | b) = 120 (ASCII 'x').


 120 - 45 ('-') = 75 → ASCII 75 = 'K'.

3. char e = (a ^ b) + '+';
Bitwise XOR (^) between a and b:

 (a ^ b) = 40 (ASCII '(').
 40 + 43 ('+') = 83 → ASCII 83 = 'S'.
Step 3: Final Values of c, d, e
 c = 'z' (ASCII 122)
 d = 'K' (ASCII 75)
 e = 'S' (ASCII 83)

Step 4: Output

Key Takeaways:
1. Bitwise Operators with ASCII Values:
o & → Preserves common bits.
o | → Combines bits.
o ^ → Toggles bits.
2. Operator Precedence and Arithmetic:
o Bitwise operations occur before arithmetic.
o ASCII manipulation is crucial for understanding character-based outputs.
3. Practical Implication:
o Even simple bitwise operations on characters can yield meaningful ASCII
outputs that align with the character set.

Extra info
Types of Bitwise Operators:
1. & (Bitwise AND):
o Compares each bit of two operands.
o Returns 1 if both bits are 1.
o Returns 0 if any of the bits are 0.
2. | (Bitwise OR):
 Compares each bit of two operands.
 Returns 1 if at least one of the bits is 1.
 Returns 0 if both bits are 0.

3. ^ (Bitwise XOR):
 Compares each bit of two operands.
 Returns 1 if the bits are different.
 Returns 0 if the bits are the same.
How to Convert to Binary (Easily):

You might also like