CSS Prepare

optional · CSS study notes

🔥 Most Repeated — Programming Fundamentals

CSS Computer Science (Optional) — Paper-I, Section-A, Topic II. These 9 coding/theory patterns recur across FPSC past papers (2016, 2017, 2018, 2019, 2020, 2022, 2024). Every coding question includes a complete, compilable C++ solution plus the reasoning an examiner is checking for.

Q1. Temperature Conversion (Celsius ↔ Fahrenheit, menu-driven)

image.png

Appeared: 2016, Q2(a); 2024, Q4(b)

Write a program that prompts the user to choose a conversion direction (Fahrenheit→Celsius or Celsius→Fahrenheit), inputs the temperature, and displays the converted value.

Solution:

#include <iostream>
using namespace std;

int main()
{
    int choice;
    double temp, result;

    cout << "1. Fahrenheit to Celsius\n2. Celsius to Fahrenheit\nEnter choice: ";
    cin >> choice;

    if (choice == 1)
    {
        cout << "Enter temperature in Fahrenheit: ";
        cin >> temp;
        result = (temp - 32) * 5.0 / 9.0;
        cout << temp << " F = " << result << " C" << endl;
    }
    else if (choice == 2)
    {
        cout << "Enter temperature in Celsius: ";
        cin >> temp;
        result = (temp * 9.0 / 5.0) + 32;
        cout << temp << " C = " << result << " F" << endl;
    }
    else
    {
        cout << "Invalid choice." << endl;
    }
    return 0;
}

Key reasoning points: use 5.0/9.0 (not 5/9, which truncates to 0 in integer division) — this is the single most common bug examiners look for; wrap both directions in one menu-driven program rather than writing two separate programs, since "prompts the user for a choice" is explicitly part of the question.


Q2. Grade Calculator (marks → average → letter grade, and the reverse)

Appeared: 2018, Q3(a) — marks to grade; 2024, Q2(a) — letter to numeric

(a) Input marks for 5 subjects, compute the average, and display a letter grade with a contextual message.

(b) Input a letter grade (A/B/C/D/F) and display its numeric value (4/3/2/1/0).

Solution (a):

#include <iostream>
using namespace std;

int main()
{
    double marks[5], sum = 0, average;
    for (int i = 0; i < 5; i++)
    {
        cout << "Enter marks for subject " << i + 1 << ": ";
        cin >> marks[i];
        sum += marks[i];
    }
    average = sum / 5;

    char grade;
    if (average >= 90) grade = 'A';
    else if (average >= 80) grade = 'B';
    else if (average >= 70) grade = 'C';
    else if (average >= 60) grade = 'D';
    else grade = 'F';

    cout << "Average: " << average << ", Grade: " << grade << endl;
    if (grade == 'F')
        cout << "You need to improve significantly." << endl;
    else
        cout << "Good job, keep it up!" << endl;
    return 0;
}

Solution (b):

#include <iostream>
using namespace std;

int main()
{
    char grade;
    int value;
    cout << "Enter letter grade (A/B/C/D/F): ";
    cin >> grade;

    switch (grade)
    {
        case 'A': value = 4; break;
        case 'B': value = 3; break;
        case 'C': value = 2; break;
        case 'D': value = 1; break;
        case 'F': value = 0; break;
        default:
            cout << "Invalid grade." << endl;
            return 0;
    }
    cout << "Numeric value: " << value << endl;
    return 0;
}

Key reasoning points: this family of question always tests the same skill — mapping a continuous or categorical input to a fixed set of output bands using if-else if or switch. Recognize which direction is being asked (value→grade needs if-else ranges; grade→value needs a switch/lookup) and don't mix them up under exam pressure.


Q3. Fibonacci Series up to N

Appeared: 2020 (CSS-2020)

Input a number from the user and print the Fibonacci series up to that number.

Solution:

#include <iostream>
using namespace std;

int main()
{
    int n, a = 0, b = 1, next;
    cout << "Enter a number: ";
    cin >> n;

    cout << "Fibonacci series up to " << n << ": ";
    while (a <= n)
    {
        cout << a << " ";
        next = a + b;
        a = b;
        b = next;
    }
    cout << endl;
    return 0;
}

Key reasoning points: "up to N" is a ceiling (loop while the term is not greater than N), not a count of terms — confusing this with "first N terms" (which needs a for loop running exactly n times) is the classic trap. A full trace table, the recursive variant, and an optimized (fast-doubling) version already live on the Source to Exe File Conversion Process page, where this question is treated in more depth.


Q4. Palindrome Check

Appeared: 2019, Q4(a)

Write a C++ program to check whether a string is a palindrome or not.

Solution:

#include <iostream>
#include <string>
using namespace std;

int main()
{
    string s;
    cout << "Enter a string: ";
    cin >> s;

    int left = 0, right = s.length() - 1;
    bool isPalindrome = true;

    while (left < right)
    {
        if (s[left] != s[right])
        {
            isPalindrome = false;
            break;
        }
        left++;
        right--;
    }

    if (isPalindrome)
        cout << s << " is a palindrome." << endl;
    else
        cout << s << " is not a palindrome." << endl;
    return 0;
}

Key reasoning points: the two-pointer approach (compare first/last, move inward) runs in O(n) time and O(1) extra space — stronger than building a reversed copy of the string and comparing, which uses O(n) extra space. Mentioning this tradeoff earns extra credit in a CSS answer.


Q5. Pattern-Printing Function (state the return type)

Appeared: 2018, Q3(e); 2022, Q2(a)

Write a function pattern(int n) that prints a triangular number pattern. State the function's return type and pay attention to spacing.

Solution:

#include <iostream>
using namespace std;

void pattern(int n)   // return type: void -- the function prints directly, it has nothing to hand back to the caller
{
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= i; j++)
            cout << j << " ";
        cout << endl;
    }
}

int main()
{
    int n;
    cout << "Enter n: ";
    cin >> n;
    pattern(n);
    return 0;
}

Sample output (n = 4):

1
1 2
1 2 3
1 2 3 4

Key reasoning points: the outer loop controls the row (1 to n), the inner loop controls how many numbers print on that row (1 to i) — this "inner loop bound depends on outer loop variable" structure is the core idea behind every triangular pattern question, whether it prints numbers, stars, or letters. The examiner explicitly asks for the return type because void (print inside the function) vs a function that builds and returns a string/array are both valid designs — stating your choice and why shows deliberate design thinking.


Q6. Debugging: Logical Errors in a Prime-Number Checker

Appeared: 2020, Q3(a)

Identify the logical errors in the given C++ program (checks whether a number is prime) and provide corrected statements.

Buggy version (typical form given in the paper):

bool isPrime(int n)
{
    for (int i = 2; i <= n; i++)     // BUG 1: loop should stop before i == n
    {
        if (n % i == 0)
            return false;
    }
    return true;                     // BUG 2: n = 1 and n = 0 incorrectly pass as "prime"
}

Corrected version:

bool isPrime(int n)
{
    if (n < 2) return false;               // fix: 0 and 1 are not prime
    for (int i = 2; i * i <= n; i++)        // fix: only test up to sqrt(n); also stops i from reaching n itself
    {
        if (n % i == 0)
            return false;
    }
    return true;
}

Key reasoning points (the errors, named explicitly):

  1. The loop condition i <= n lets i reach n, and n % n is always 0, so the function would incorrectly report every number as non-prime once i reaches n — it should stop earlier.
  2. There is no check excluding n < 2: by definition 0 and 1 are not prime, but the buggy version's loop for n = 1 never executes and falls through to return true.
  3. (Efficiency fix, often awarded bonus marks) looping only while i * i <= n is mathematically sufficient — if n has a factor greater than its square root, it must also have one smaller than the square root, so checking beyond sqrt(n) is redundant.

Q7. Call by Value vs Call by Reference

Appeared: 2020, Q3(b)

What is the difference between call by value and call by reference?

Solution:

#include <iostream>
using namespace std;

void byValue(int x)      { x = x + 10; }        // modifies only the local copy
void byReference(int &x) { x = x + 10; }         // modifies the caller's original variable

int main()
{
    int a = 5, b = 5;
    byValue(a);
    byReference(b);
    cout << "a = " << a << " (unchanged), b = " << b << " (changed)" << endl;
    return 0;
}

Output: a = 5 (unchanged), b = 15 (changed)

Key reasoning points: call by value copies the argument into the function's own stack memory — changes inside the function never reach the caller. Call by reference (&) binds the parameter directly to the caller's variable, so the function operates on the original — useful whenever a function needs to modify multiple values or avoid the overhead of copying large objects.


Q8. Role of Preprocessor Directives

Appeared: 2020, Q3(c)

What is the role of preprocessor directives? Give three examples in C++.

Solution:

Preprocessor directives are instructions processed by the preprocessor before actual compilation begins. They start with #, are not C++ statements (no semicolon), and operate on the source text itself (textual substitution/inclusion), not on runtime values.

#include <iostream>      // (1) includes the contents of a header file into this source file

#define PI 3.14159        // (2) defines a macro -- every occurrence of PI is textually replaced with 3.14159 before compiling

#ifndef MYHEADER_H         // (3) include guard -- prevents a header from being included (and its contents redefined) more than once
#define MYHEADER_H
// ... header contents ...
#endif

Key reasoning points: a common trap is treating #define like a typed constant — it is pure text substitution with no type checking, which is exactly why modern C++ generally prefers const/constexpr for constants, reserving macros for conditional compilation and include guards.


Q9. Function Writing: Prototype + Definition + Demonstration (Quadratic Equation)

Appeared: 2017, Q3(b) — quadratic equation; 2016, Q3(a) — monthly-profit function (same pattern)

Write a function that calculates the roots of a quadratic equation given coefficients a, b, c, entered by the user. Show the function prototype, definition, and a demonstration in main().

Solution:

#include <iostream>
#include <cmath>
using namespace std;

void solveQuadratic(double a, double b, double c);   // prototype

int main()
{
    double a, b, c;
    cout << "Enter coefficients a, b, c: ";
    cin >> a >> b >> c;
    solveQuadratic(a, b, c);                          // demonstration call
    return 0;
}

void solveQuadratic(double a, double b, double c)      // definition
{
    if (a == 0) { cout << "Not a quadratic equation (a cannot be 0)." << endl; return; }

    double discriminant = b * b - 4 * a * c;

    if (discriminant > 0)
    {
        double r1 = (-b + sqrt(discriminant)) / (2 * a);
        double r2 = (-b - sqrt(discriminant)) / (2 * a);
        cout << "Two real roots: " << r1 << " and " << r2 << endl;
    }
    else if (discriminant == 0)
    {
        double r = -b / (2 * a);
        cout << "One repeated real root: " << r << endl;
    }
    else
    {
        double realPart = -b / (2 * a);
        double imagPart = sqrt(-discriminant) / (2 * a);
        cout << "Two complex roots: " << realPart << " + " << imagPart << "i and "
             << realPart << " - " << imagPart << "i" << endl;
    }
}

Key reasoning points: the examiner is really testing three separate things at once — correct prototype syntax (declaration ending in a semicolon, matching the definition's signature), a correct definition, and a correct call site. The discriminant's three cases (positive/zero/negative) are the mathematical core and must all be handled, not just the "nice" positive case. The 2016 monthly-profit variant (double monthlyProfit(double investment, double profitPercent), returning investment * profitPercent / 100 / 12) is structurally identical: one function, two numeric inputs, one computed return value, demonstrated from main().


Q10. Bit Manipulation — What It Is and Why It's Used

Appeared: 2026, Q3(a)

What is bit manipulation in programming, and why is it used? Give two examples of its use.

Solution:

Bit manipulation is the technique of directly operating on the individual bits of data using bitwise operators (&, |, ^, ~, <<, >>) instead of working through ordinary arithmetic on whole values.

Why it's used: it is extremely fast (a single CPU instruction per operation), memory-efficient (multiple boolean flags can be packed into one integer), and essential for low-level tasks such as device drivers, embedded systems, cryptography, and graphics.

Two examples:

  1. Checking even/odd: if (n & 1) { /* odd */ } else { /* even */ } — faster than n % 2, since it just inspects the last bit.
  2. Setting/clearing a flag bit in a permissions byte: permissions |= (1 << 2); sets bit 2 (e.g. a "write" permission) without touching any other bit; permissions &= ~(1 << 2); clears that same bit.

Cross-reference: full coverage of shifts, masks, and set/clear/toggle/check operations lives on the "Structures, Unions, and Bit Manipulation" page.


Q11. Arrays — One Advantage, One Limitation

Appeared: 2026, Q3(b)

What are arrays? Describe one advantage and one limitation.

Solution:

An array is a fixed-size, contiguous collection of elements of the same data type, accessed via a zero-based index.

int marks[5] = {90, 85, 76, 60, 99};
cout << marks[2];   // 76

Advantage: constant-time O(1) random access to any element via its index (address = base_address + index * element_size) — no traversal required.

Limitation: fixed size, set at declaration — a static array cannot grow or shrink at runtime; inserting or deleting an element in the middle requires shifting every subsequent element, an O(n) operation.


Q12. Pseudocode — Reverse an Array Using Pointers

Appeared: 2026, Q3(c)

Write pseudocode to reverse an array using pointers.

Solution:

ALGORITHM ReverseArray(arr, n)
    start ← pointer to arr[0]
    end   ← pointer to arr[n-1]
    WHILE start < end DO
        temp   ← *start
        *start ← *end
        *end   ← temp
        start  ← start + 1
        end    ← end - 1
    END WHILE
END ALGORITHM

Equivalent C++:

void reverseArray(int* arr, int n)
{
    int* start = arr;
    int* end = arr + n - 1;
    while (start < end)
    {
        int temp = *start;
        *start = *end;
        *end = temp;
        start++;
        end--;
    }
}

Key reasoning points: the two-pointer technique runs in O(n) time and O(1) extra space — reversal happens in place, with no second array needed. Using pointer arithmetic (arr + n - 1, *start, *end) rather than plain indices is exactly what "using pointers" is asking for.


Frequency Note

Papers reviewed: 2016, 2017, 2018, 2019, 2020, 2022, 2024, 2026. Almost every year contains at least one "write a C++ program that does X given a user-input scenario" question, one function-design question (prototype + definition + demo), and — increasingly in recent papers — a pointer/array manipulation question. See also the Exam Practice Index on the main Programming Fundamentals page for additional worked examples (array/string manipulation, bit manipulation).


2026 Past Paper MCQs (Part-I)

  1. A programmer wants to group different data types. What should he/she use?

    A) Structures B) Arrays C) Unions D) None of these

    Why: an array only holds elements of one type; a structure groups several different types under one name (a union also groups different types, but they share the same memory rather than all being held at once — "Structures" is the intended general-purpose answer).

  2. A function needs to modify an input variable. How should the parameter be passed?

    A) By value B) By reference C) By pointer D) None of these

    Why: passing by pointer also technically works, but "by reference" is the direct, idiomatic C++ answer — see Q7 above on this same page for the full call-by-value vs call-by-reference comparison.