Gate Notes Realted To Programming
Gate Notes Realted To Programming
1
Key Concepts of Storage Classes:
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:
3. Scope:
The scope defines the region of the program where the variable is accessible.
o Program scope: Accessible across the entire program (e.g., global variables).
Example:
o Function scope: Created when the function is entered, and destroyed when the
function exits.
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 EXAMPLE:
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() {
count++;
int main() {
return 0;
Output:
Explanation:
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.
Output Explanation:
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.
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>
void displayGlobalVariable();
int main() {
printf("In main.c, globalVariable = %d\n", globalVariable);
displayGlobalVariable();
return 0;
}
o EXAMPLE 2:
// helper.c
#include <stdio.h>
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:
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
+-----------------------+
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
Printing i in main
printf("%d\n", i); prints 5.
Program Termination
main returns.
main's stack frame is destroyed.
The program ends.
Stack Growth:
o On many systems, the stack grows downward (from higher to lower memory
addresses).
25- 9 -2024
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)
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:
Computation in abc:
k = k + m → k = 10 + 5 → k = 15.
o After Update:
o Output:
Prints 15.
Computation in abc:
k = k + m → k = 15 + 5 → k = 20.
After Update:
Output:
Prints 20.
Stack after Function Returns:
Note: The second printf("%d", a); will execute after the recursive call returns.
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.
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.
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.
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
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
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
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.
#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.
#include <global.c>
2. Compile main.c:
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
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;
}
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.
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
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
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:
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.
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().
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:
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).
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:
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.
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:
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)
This confirms that 31 function calls are made when computing F(5).
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
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:
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.
2. Second Call:
3. Third Call:
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}.
So:
Right Side:
Additional Notes:
Understanding Logarithms in This Context
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.
Recurrence Relation:
When
The function makes a recursive call with N/RN / RN/R. Therefore:
Third Call:
So,
Solving for k:
Taking logarithms:
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
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
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.
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.
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).
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)
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.
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:
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:
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.
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.
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.
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
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.
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
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.
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:
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:
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:
Accessing Values:
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.
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.
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.
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
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.
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.
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.
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
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.
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
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
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:
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
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);
Memory Representation
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>
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
Problem 2
Code
#include <stdio.h>
int main() {
int c = 4;
int *a;
int **b;
Calling Function f
int result = f(c, a, b);
Arguments Passed:
o x = 4;
o py = a; (address of c ⇒ 2000)
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
3. Variable Pointer:
To navigate through the array elements, use a variable pointer.
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
*.
3. Subscript Rule:
Accessing elements using subscript notation can be expressed using pointers.
4. Equivalent Expressions:
The following expressions are equivalent in accessing array elements:
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
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.
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:
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:
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 Declaration
Assignment
Or
Pointer Arithmetic
When you increment the pointer:
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).
Assignment
p = a; // 'p' points to the first two-dimensional array in 'a'
Pointer Arithmetic
When you increment the pointer:
o Incrementation:
Incrementation:
Incrementation:
p = p + 1; // Moves by N * M * sizeof(int) bytes
Two-Dimensional Array
Value at a[i][j]:
Address of a[i][j]:
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:
Pointer Declaration
Assignment
Pointer Arithmetic
Accessing Elements
Access a[1][1][2] using pointers:
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:
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:
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)
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.
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].
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:
Loop Conditions:
Initializing Elements:
We initialize each element with:
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:
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):
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
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 **.
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.
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
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.
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).
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:
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:
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.
Here:
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
(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:
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.
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;
}
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
int main() {
// Four string constants stored in static memory
const char *S[4] = {"ice", "green", "cone", "please"};
return 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".
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:
Explanation:
Creates an array a of three Student structures.
Each structure contains:
o idno (integer).
o s (character array).
Accessing Members:
Using array:
(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
S2 Layout:
Updated Layout:
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:
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:
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).
Answer:
Options:
a) 1204567
b) 1234567
c) 1204567 (in single quotes)
d) 1200000
Explanation:
1. Memory Allocation and Initialization:
3. Modification:
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:
2. char d = (a | b) - '-';
Bitwise OR (|) between a and b:
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):