PPS-02: Alternate Question Bank with MCQs

Comprehensive Zero-Mark Deduction Solutions

Question 1 (Marks: 2)
For a 16-bit compiler, what is the allowable range for integer constants?
  • (A) -3.4e38 to 3.4e38
  • (B) -32767 to 32768
  • (C) -32768 to 32767
  • (D) -32668 to 32667
Click to Reveal Answer & Solution
Correct Answer: (C) -32768 to 32767
Examiner's Detailed Solution:
Step 1: A 16-bit compiler allocates exactly 16 bits (or 2 bytes) for a standard signed `int` data type.
Step 2: The total number of unique values that can be represented is \( 2^{16} = 65536 \).
Step 3: Since C uses Two's Complement representation for signed integers, one bit (the most significant bit or MSB) is reserved for the sign (0 for positive, 1 for negative).
Step 4: The formula for the range of an n-bit signed integer is \( -2^{n-1} \) to \( 2^{n-1} - 1 \).
Step 5: Substituting \( n = 16 \): The lower bound is \( -2^{15} = -32768 \). The upper bound is \( 2^{15} - 1 = 32768 - 1 = 32767 \).
Step 6: Thus, the exact allowable range is -32768 to 32767. This prevents integer overflow/underflow within these limits.
Question 2 (Marks: 2)
Which of the following shows the correct hierarchy (precedence) of arithmetic operators in C?
  • (A) / + * -
  • (B) * - / +
  • (C) * / + -
  • (D) + - / *
Click to Reveal Answer & Solution
Correct Answer: (C) * / + -
Examiner's Detailed Solution:
Step 1: In C programming, arithmetic operators follow standard algebraic precedence (often remembered via the acronym BODMAS/PEMDAS, modified for C).
Step 2: The Multiplicative operators (`*` for multiplication, `/` for division, and `%` for modulo) share the exact same precedence level. This level is strictly higher than additive operators.
Step 3: The Additive operators (`+` for addition and `-` for subtraction) share the same precedence level, which sits immediately below the multiplicative operators.
Step 4: Because `*` and `/` possess higher precedence than `+` and `-`, any expression combining them (without parentheses) will execute the multiplication and division operations before any addition or subtraction.
Step 5: If operators of the same precedence appear adjacent to one another (e.g., `a * b / c`), C uses Left-to-Right associativity to determine the order of evaluation.
Question 3 (Marks: 2)
What is the output of the following C code snippet?
int i = 5;
printf("%d %d %d", i++, i, ++i);
  • (A) 5 6 7
  • (B) 6 6 6
  • (C) 7 7 7
  • (D) Compiler Dependent
Click to Reveal Answer & Solution
Correct Answer: (C) 7 7 7 (or Compiler Dependent depending on strict interpretation, but universally recognized in typical older GCC as 7 7 7 due to Right-to-Left argument evaluation)
Examiner's Detailed Solution:
Step 1: This question tests the concept of 'Order of Evaluation' of function arguments in C.
Step 2: In C, the order in which arguments are evaluated before being passed to a function (like `printf`) is *unspecified* by the C standard, leading to Undefined Behavior. However, historically, standard C compilers (like older GCC or Turbo C) evaluate arguments from Right to Left.
Step 3: Evaluating from Right to Left:
1. `++i` (Pre-increment): `i` is incremented to 6. The value passed is 6.
2. `i`: The current value of `i` is 6. The value passed is 6.
3. `i++` (Post-increment): The current value 6 is passed, and *then* `i` is incremented to 7.
Step 4: Wait, if evaluated Right to Left and pushed to stack: The expression evaluates as `++i` -> `i` becomes 6, passes 6 (or reference to `i`). However, because of sequence points, modifying `i` multiple times between sequence points is Undefined Behavior.
Step 5: In classic examination marking schemes (especially Indian university systems), questions like this assume Right-to-Left evaluation with post-increments resolved at the end. The standard accepted exam answer is often **7 7 7** or **6 6 7** depending on the specific legacy compiler the examiner references. Given the provided options, if it strictly adheres to standard textbook outputs (like Yashavant Kanetkar's Let Us C), it evaluates right-to-left: `++i` makes `i=6`, `i` is 6, `i++` passes 6 then makes `i=7`. But since `printf` prints the final state of `i` in some compilers, they print 7 7 7. For a zero-mark deduction, explicitly state it is Undefined Behavior, but select the provided accepted key.
Question 4 (Marks: 2)
Which of the following loops is guaranteed to execute at least once, regardless of the condition?
  • (A) for loop
  • (B) while loop
  • (C) do-while loop
  • (D) None of the above
Click to Reveal Answer & Solution
Correct Answer: (C) do-while loop
Examiner's Detailed Solution:
Step 1: Loops in C are classified into two main categories based on where the condition is checked: Entry-controlled loops and Exit-controlled loops.
Step 2: `for` and `while` loops are Entry-controlled. The boolean condition is evaluated *before* entering the loop body. If the condition is initially `false`, the loop body is bypassed entirely (executes 0 times).
Step 3: The `do-while` loop is an Exit-controlled loop. The syntax structure is:
do {
// loop body
} while (condition);
Step 4: Because the condition evaluation (`while (condition)`) is situated at the absolute end of the construct, the program flow must inherently pass through the loop body first.
Step 5: Therefore, even if the condition evaluates to `false` on the very first check, the loop body has already executed exactly one time. It is guaranteed to execute at least once.
Question 5 (Marks: 2)
What will happen if you attempt to access an array element outside its declared bounds (e.g., arr[10] for an array of size 5)?
  • (A) Compiler Error
  • (B) Runtime Error (Crash)
  • (C) Garbage Value / Undefined Behavior
  • (D) Returns 0
Click to Reveal Answer & Solution
Correct Answer: (C) Garbage Value / Undefined Behavior
Examiner's Detailed Solution:
Step 1: In C, array indices are simply calculated as memory offsets from the base address of the array (`Base_Address + index * sizeof(DataType)`).
Step 2: Crucially, the C compiler **does not perform bounds checking** (either at compile-time or run-time) to verify if the index falls within the declared size. This is a design choice to maximize execution speed and efficiency.
Step 3: When you access `arr[10]`, the program calculates the corresponding memory address and blindly reads (or writes) the data at that specific memory location.
Step 4: If that memory belongs to another variable, you will read a 'garbage value' (from the array's perspective). If you write to it, you corrupt other data, which may eventually lead to a Segmentation Fault, but it is not guaranteed to crash immediately.
Step 5: Therefore, the exact result is formally classified as Undefined Behavior (UB), which typically manifests as retrieving unpredictable garbage values.
Question 6 (Marks: 2)
Consider the declaration int *ptr, p;. What are the types of ptr and p?
  • (A) Both are integer pointers
  • (B) ptr is an integer pointer, p is a standard integer
  • (C) Both are standard integers
  • (D) ptr is an integer, p is a pointer
Click to Reveal Answer & Solution
Correct Answer: (B) ptr is an integer pointer, p is a standard integer
Examiner's Detailed Solution:
Step 1: In C, the asterisk (`*`) operator used during variable declaration binds tightly to the specific variable name it precedes, not to the base data type itself.
Step 2: When the compiler parses the statement `int *ptr, p;`, it reads it as declaring two separate variables of the base type `int`.
Step 3: For `ptr`, the asterisk indicates that it is a pointer, so `ptr` becomes a variable that holds the memory address of an integer (`int *`).
Step 4: For `p`, there is no asterisk preceding it. Therefore, it is treated as a regular, non-pointer variable of the base type `int`.
Step 5: To declare both as pointers in a single line, one must explicitly write `int *ptr, *p;`. Failure to do so leads to this common trap.
Question 7 (Marks: 2)
Which of the following functions is used to dynamically allocate memory for an array and simultaneously initialize all its bits to zero?
  • (A) malloc()
  • (B) realloc()
  • (C) calloc()
  • (D) free()
Click to Reveal Answer & Solution
Correct Answer: (C) calloc()
Examiner's Detailed Solution:
Step 1: Dynamic memory allocation in C is handled via functions in the `` library.
Step 2: `malloc(size_t size)` allocates a single block of contiguous memory of the requested size. Crucially, it leaves the allocated memory uninitialized, meaning it will contain unpredictable garbage values.
Step 3: `calloc(size_t nitems, size_t size)` stands for 'contiguous allocation'. It takes two arguments: the number of elements and the size of each element.
Step 4: The defining characteristic of `calloc()` that distinguishes it from `malloc()` is that it automatically initializes every bit of the allocated memory block to zero immediately after allocation.
Step 5: Therefore, `calloc` is the correct and safest choice when you require a dynamically allocated array initialized to zero to prevent non-deterministic behavior.
Question 8 (Marks: 2)
How is a structure member accessed when using a pointer to a structure?
  • (A) Using the dot (.) operator
  • (B) Using the ampersand (&) operator
  • (C) Using the arrow (->) operator
  • (D) Using the asterisk (*) operator
Click to Reveal Answer & Solution
Correct Answer: (C) Using the arrow (->) operator
Examiner's Detailed Solution:
Step 1: In C, structures group logically related variables under a single name.
Step 2: When dealing with a standard structure variable (e.g., `struct Student s1;`), individual members are accessed using the Direct Member Access Operator, which is the dot (`.`). Example: `s1.roll_no`.
Step 3: However, when dealing with a pointer that holds the address of a structure (e.g., `struct Student *ptr = &s1;`), you cannot use the dot operator directly on the pointer because the pointer itself is just a memory address.
Step 4: To access the member, you must first dereference the pointer (`*ptr`) and then use the dot operator: `(*ptr).roll_no`. Note the parentheses are mandatory due to operator precedence.
Step 5: To simplify this verbose syntax, C provides the Indirect Member Access Operator, commonly known as the arrow operator (`->`). Therefore, `ptr->roll_no` is the elegant, standard, and correct way to access a member via a structure pointer.
Question 9 (Marks: 2)
What is the primary difference between a `struct` and a `union` in C?
  • (A) Structs can only hold integers, unions can hold any type.
  • (B) In a struct, all members share the same memory location. In a union, each member has its own memory.
  • (C) In a struct, each member has its own separate memory location. In a union, all members share the exact same memory location.
  • (D) There is no difference, they are synonyms.
Click to Reveal Answer & Solution
Correct Answer: (C) In a struct, each member has its own separate memory location. In a union, all members share the exact same memory location.
Examiner's Detailed Solution:
Step 1: Both `struct` and `union` are user-defined composite data types in C used to group disparate data types together.
Step 2: **Memory Allocation of Struct:** A `struct` allocates separate, contiguous blocks of memory for each individual member. The total size of the struct is (at minimum) the sum of the sizes of all its individual members (plus potential padding). You can access and modify all members independently at the same time.
Step 3: **Memory Allocation of Union:** A `union` allocates only a single shared block of memory, whose size is strictly equal to the size of its largest individual member.
Step 4: Because all members in a union physically map to the same starting memory address, writing data to one member will overwrite and corrupt the data stored in any other member.
Step 5: Thus, a union can only safely hold a value for *one* of its members at any given moment in time, making it highly memory-efficient when mutually exclusive data types are needed.
Question 10 (Marks: 2)
Which mode is used in the `fopen()` function to append new data to the end of an existing file without deleting its current contents?
  • (A) "w"
  • (B) "r+"
  • (C) "a"
  • (D) "w+"
Click to Reveal Answer & Solution
Correct Answer: (C) "a"
Examiner's Detailed Solution:
Step 1: The `fopen()` function in C opens a file and requires two string arguments: the filename and the file access mode.
Step 2: **"w" (Write Mode):** Opens a file for writing. If the file already exists, this mode brutally truncates (erases) all existing content before writing. If it doesn't exist, it creates it.
Step 3: **"r+" (Read/Update Mode):** Opens an existing file for both reading and writing. The file pointer is placed at the beginning, meaning subsequent writes will overwrite existing data from the start.
Step 4: **"a" (Append Mode):** Opens a file strictly for writing. Crucially, the internal file pointer is automatically positioned at the absolute end (EOF) of the file. Any data written will be appended after the existing content, perfectly preserving the original data.
Step 5: Therefore, "a" is the exact and correct mode required for safe data appending.