C Programming
Deep Understanding: 70 hours
Community
C Programming
2081 Boards
Section A
Answer any two questions.
Different types of operators are used to perform operations on variables and values.
The common types of operators include:
- Arithmetic Operators
- Relational Operators
- Logical Operators
- Assignment Operators
- Bitwise Operators
- Unary Operators
- Ternary/Conditional Operator
- Special Operators (e.g.,
sizeof, address-of&, dereference*, member access.and->)
Explanation of four types of operators:
-
Arithmetic Operators
These operators perform basic mathematical calculations. They operate on numerical operands and return a numerical result.- Operators:
+(Addition): Adds two operands.-(Subtraction): Subtracts the second operand from the first.*(Multiplication): Multiplies two operands./(Division): Divides the first operand by the second. For integer division, it truncates the fractional part.%(Modulus): Returns the remainder of an integer division.
- Example:
int a = 10, b = 3; int sum = a + b; // sum is 13 int remainder = a % b; // remainder is 1
- Operators:
-
Relational Operators
These operators compare two operands and return a Boolean result (true or false, often represented as 1 or 0 in languages like C/C++). They are primarily used in conditional statements and loops.- Operators:
==(Equal to): Returns true if both operands are equal.!=(Not equal to): Returns true if operands are not equal.>(Greater than): Returns true if the first operand is greater than the second.<(Less than): Returns true if the first operand is less than the second.>=(Greater than or equal to): Returns true if the first operand is greater than or equal to the second.<=(Less than or equal to): Returns true if the first operand is less than or equal to the second.
- Example:
int x = 20, y = 15; bool is_greater = (x > y); // is_greater is true (1) bool are_equal = (x == y); // are_equal is false (0)
- Operators:
-
Logical Operators
These operators are used to combine or negate Boolean expressions. They evaluate conditions and return a Boolean result (true or false).- Operators:
&&(Logical AND): Returns true if both operands are true.||(Logical OR): Returns true if at least one operand is true.!(Logical NOT): Reverses the logical state of its operand (true becomes false, false becomes true).
- Example:
bool condition1 = true, condition2 = false; bool result_and = condition1 && condition2; // result_and is false bool result_or = condition1 || condition2; // result_or is true bool result_not = !condition1; // result_not is false
- Operators:
-
Assignment Operators
Assignment operators are used to assign a value to a variable. The simple assignment operator=assigns the value on the right to the variable on the left. Compound assignment operators combine an arithmetic (or bitwise) operation with assignment, providing a shorthand for common operations.- Operators:
=(Simple Assignment): Assigns the value of the right operand to the left operand.+=(Add and Assign):x += yis equivalent tox = x + y.-=(Subtract and Assign):x -= yis equivalent tox = x - y.*=(Multiply and Assign):x *= yis equivalent tox = x * y./=(Divide and Assign):x /= yis equivalent tox = x / y.%=(Modulus and Assign):x %= yis equivalent tox = x % y.
- Example:
int value = 5; value += 3; // value is now 8 (equivalent to value = 5 + 3) value *= 2; // value is now 16 (equivalent to value = 8 * 2)
- Operators:
Characteristics of Array
An array is a linear data structure that stores a collection of elements of the same data type at contiguous memory locations. It is one of the simplest and most fundamental data structures.
Key characteristics include:
- Homogeneous Data Type: All elements stored in an array must be of the same data type (e.g., all integers, all floats, or all characters).
- Fixed Size: The size of an array (the maximum number of elements it can hold) is typically defined at compile time or upon declaration and cannot be changed during program execution (for static arrays).
- Contiguous Memory Allocation: Array elements are stored in sequential memory locations. This allows for efficient access and manipulation of elements.
- Indexed Access: Each element in an array is identified by a unique index or subscript. This index is used to directly access or modify individual elements.
- Zero-Based Indexing: In most programming languages (like C, C++, Java), arrays are zero-indexed, meaning the first element is at index 0, the second at index 1, and so on, up to
size - 1. - Direct/Random Access: Due to contiguous memory allocation and indexing, any element of an array can be accessed directly in constant time, irrespective of its position. This is also known as random access.
- Static Nature: Arrays generally reside in the static or stack memory region, depending on their declaration, and their memory is allocated during compilation or function call. Dynamic arrays (like
std::vectorin C++) can resize at runtime but manage underlying static arrays.
Program to input age of 500 persons and display average age and ages between 25 to 30
#include <iostream> // Required for standard input/output operations (cin, cout)
#include <iomanip> // Required for output formatting (std::fixed, std::setprecision)
// Define a constant for the number of persons for clarity and easy modification.
const int NUM_PERSONS = 500;
int main() {
// Declare an integer array to store the ages of 500 persons.
int ages[NUM_PERSONS];
// Initialize a variable to accumulate the sum of all ages.
// Using 'long long' to prevent potential overflow if ages are large and NUM_PERSONS is high.
long long sumOfAges = 0;
// Initialize a counter for persons whose age falls within the specified range [25, 30].
int countAges25_30 = 0;
// --- Input Phase ---
std::cout << "Please enter the age for " << NUM_PERSONS << " persons:\n";
for (int i = 0; i < NUM_PERSONS; ++i) {
std::cout << "Enter age for person " << (i + 1) << ": ";
std::cin >> ages[i]; // Read the age from the user and store it in the array.
// Add the current age to the running total sum.
sumOfAges += ages[i];
// Check if the current age is within the inclusive range of 25 to 30.
if (ages[i] >= 25 && ages[i] <= 30) {
countAges25_30++; // Increment the counter if the condition is met.
}
}
// --- Output Phase ---
// 1. Calculate and display the Average age.
// Perform floating-point division by casting sumOfAges to double.
double averageAge = static_cast<double>(sumOfAges) / NUM_PERSONS;
std::cout << "\n--- Analysis Results ---\n";
// Set output formatting for the average age to two decimal places.
std::cout << std::fixed << std::setprecision(2);
std::cout << "Average age of " << NUM_PERSONS << " persons: " << averageAge << " years.\n";
// 2. Display Ages between 25 to 30.
std::cout << "\nPersons with age between 25 and 30 (inclusive):\n";
if (countAges25_30 == 0) {
std::cout << " No persons found with age between 25 and 30.\n";
} else {
// Iterate through the array again to print the specific ages.
bool firstAgePrinted = false; // Flag to handle comma separation for readability.
std::cout << " [";
for (int i = 0; i < NUM_PERSONS; ++i) {
if (ages[i] >= 25 && ages[i] <= 30) {
if (firstAgePrinted) {
std::cout << ", "; // Add a comma if it's not the first age being printed.
}
std::cout << ages[i]; // Print the age.
firstAgePrinted = true;
}
}
std::cout << "]\n";
std::cout << " Total count of persons with age between 25 and 30: " << countAges25_30 << ".\n";
}
return 0; // Indicate successful program execution.
}
Differentiation between Library Function and User-Defined Function
-
Library Functions:
- Definition: Pre-defined functions that are part of the programming language's standard library. They are already compiled and stored in header files.
- Availability: Accessible by including the corresponding header file (e.g.,
<stdio.h>,<math.h>,<stdlib.h>). - Purpose: Provide common, frequently used functionalities like input/output operations, mathematical calculations, string manipulations, memory management, etc., to simplify programming and reduce development time.
- Control/Customization: Programmers cannot modify their internal implementation. They can only use them as provided.
- Examples:
printf(),scanf(),sqrt(),strlen(),malloc().
-
User-Defined Functions:
- Definition: Functions created by the programmer to perform specific tasks as per the program's requirements.
- Availability: Must be declared (prototyped) and defined by the programmer before they can be called.
- Purpose: Break down a complex problem into smaller, manageable sub-problems, promote modularity, reusability of code, and improve program readability and maintainability.
- Control/Customization: Programmers have complete control over their implementation, including their parameters, return type, and the logic executed inside them.
- Examples:
swap(),calculateArea(),displayMenu(), specific validation functions.
Program to Swap Two Values using Call By Reference
#include <stdio.h> // Required for printf and scanf
// Function definition for swapping two integers using call by reference
// It takes two integer pointers as arguments
void swap(int *a, int *b) {
int temp; // Declare a temporary variable
temp = *a; // Store the value pointed to by 'a' in temp
*a = *b; // Assign the value pointed to by 'b' to the location pointed to by 'a'
*b = temp; // Assign the value in temp to the location pointed to by 'b'
}
int main() {
int num1 = 10; // Declare and initialize first integer
int num2 = 20; // Declare and initialize second integer
printf("Before swapping:\n");
printf("Num1 = %d, Num2 = %d\n", num1, num2);
// Call the swap function, passing the addresses of num1 and num2
// '&' operator gets the memory address of the variables
swap(&num1, &num2);
printf("\nAfter swapping (using call by reference):\n");
printf("Num1 = %d, Num2 = %d\n", num1, num2);
return 0; // Indicate successful execution
}
Explanation of Call By Reference:
In call by reference, the memory addresses of the actual arguments are passed to the formal parameters of the function. This means the function works directly on the original variables in the calling function's memory space. Any changes made to the parameters inside the function will directly affect the original variables. In the example, swap(&num1, &num2) passes the addresses of num1 and num2. Inside swap(), *a and *b dereference these addresses to access and modify the actual values of num1 and num2, thereby achieving a permanent swap.
Section B
Answer any two questions.
The basic structure of a C program typically consists of the following components:
-
Preprocessor Directives: These commands begin with a
#symbol and are processed before the actual compilation. Common directives include#includefor including standard library header files (e.g.,<stdio.h>,<stdlib.h>) and#definefor defining symbolic constants or macros. -
Global Declarations: This section is used to declare global variables and user-defined functions that can be accessed from any part of the program. Global variables are initialized to zero by default if not explicitly assigned a value.
-
main()Function: This is the entry point of every C program. Execution always begins inside themain()function. It typically has the signatureint main()orint main(int argc, char *argv[]).- Local Declarations: Variables specific to the
main()function are declared here. These variables have local scope, meaning they are only accessible withinmain(). - Executable Statements: This block contains the actual program logic, computations, input/output operations, and function calls.
- Local Declarations: Variables specific to the
-
User-Defined Functions (Optional): After or before
main(), programmers can define their own functions to perform specific tasks. These functions must be declared (prototyped) before they are called, typically in the global declaration section.
Formatted Input and Output Functions:
Formatted input and output functions facilitate the reading and writing of data in a human-readable and specific format, typically involving conversions between internal binary representations and character sequences.
Formatted Input Functions:
These functions read data from a source (e.g., keyboard, file, string) according to a specified format string and store it into variables.
scanf(): Reads formatted input from the standard input device (keyboard).- Example:
scanf("%d %f", &integer_var, &float_var);
- Example:
fscanf(): Reads formatted input from a specified file stream.- Example:
fscanf(file_ptr, "%s", string_buffer);
- Example:
sscanf(): Reads formatted input from a string buffer.- Example:
sscanf(input_string, "Name: %s Age: %d", name, &age);
- Example:
These functions write data to a destination (e.g., console, file, string) by converting internal variable values into a character sequence based on a specified format string.
printf(): Writes formatted output to the standard output device (console).- Example:
printf("Value: %d\n", my_value);
- Example:
fprintf(): Writes formatted output to a specified file stream.- Example:
fprintf(file_ptr, "Error code: %d", error_code);
- Example:
sprintf(): Writes formatted output to a character array (string buffer).- Example:
sprintf(output_buffer, "Result is: %.2f", result);
- Example:
Why we use them:
- User-friendly Interaction: They allow programs to present information to users and accept input from them in a structured, readable, and understandable manner, enhancing the user experience.
- Data Type Conversion: These functions automatically handle the conversion between the internal binary representation of data types (e.g.,
int,float,char) and their human-readable string representations during input and output operations. - Flexible Formatting: They provide powerful formatting specifiers (e.g.,
%d,%f,%s, width specifiers, precision specifiers) that enable precise control over how data is displayed (e.g., number of decimal places, field width, alignment, padding). - Data Parsing and Generation: They are crucial for parsing data from various sources (files, network streams, user input) into program variables and for generating formatted reports or data strings for storage or transmission.
#include<stdio.h>
int main(){
int n=50,start=2, i;
int isPrime;
printf("First 50 prime numbers are :\n");
while(n>0){
isPrime=1;
for(int i=2;i<start;i++){
if(start%i==0){
isPrime=0;
break;
}
}
if(isPrime){
printf("%d\n",start);
n--;
}
start++;
}
return 0;
}term_count = 0
n = 1
while term_count < 25:
if n == 7: # Skip the 7th term
n += 1
continue
first_multiplier = n + 1
second_multiplier = (2 * n) + 1
print(f"{first_multiplier} x {second_multiplier}")
term_count += 1
n += 1
A recursive function is a function that calls itself directly or indirectly to solve a problem. It solves a problem by breaking it down into smaller, similar subproblems until it reaches a simple base case that can be solved directly.
Key components of a recursive function:
- Base Case: A condition that stops the recursion, preventing infinite loops. When this condition is met, the function returns a value without making further recursive calls.
- Recursive Step: The part of the function where it calls itself with a modified input, typically moving closer to the base case.
Example: Factorial Calculation
The factorial of a non-negative integer n, denoted as n!, is the product of all positive integers less than or equal to n.
n! = n * (n-1) * (n-2) * ... * 1- Recursive definition:
n! = n * (n-1)!forn > 1, and0! = 1,1! = 1.
Pseudocode Implementation:
function factorial(n):
if n == 0 or n == 1: // Base Case
return 1
else: // Recursive Step
return n * factorial(n - 1)
Demonstration with factorial(3):
factorial(3)is called.- Since
3is not0or1, the recursive step executes:return 3 * factorial(2). factorial(2)is called.- Since
2is not0or1, the recursive step executes:return 2 * factorial(1). factorial(1)is called.- Since
1is0or1, the base case executes:return 1. - The value
1is returned tofactorial(2). factorial(2)computes2 * 1 = 2and returns2tofactorial(3).factorial(3)computes3 * 2 = 6and returns6.
Thus, factorial(3) evaluates to 6.
#include <stdio.h>
#include <string.h>
#include <stdlib.h> // For rand() and srand()
// Structure definition for STUDENT
struct STUDENT {
int SID;
char name[50];
char address[50];
float CGPA;
};
int main() {
struct STUDENT students[100]; // Array to hold 100 student records
int i;
// Seed for random number generation
srand(1); // Use a fixed seed for reproducible dummy data
// Initialize 100 student records with dummy data
for (i = 0; i < 100; i++) {
students[i].SID = 1000 + i;
sprintf(students[i].name, "Student Name %d", i + 1);
// Assign address "KTM" to some students, others to "PKR", "BKT", etc.
if (i % 5 == 0) {
strcpy(students[i].address, "KTM");
} else if (i % 3 == 0) {
strcpy(students[i].address, "PKR");
} else {
strcpy(students[i].address, "BKT");
}
// Assign CGPA between 2.0 and 4.0
students[i].CGPA = 2.0 + (float)rand() / RAND_MAX * 2.0; // CGPA between 2.0 and 4.0
}
// Display information for students whose address is "KTM" and CGPA is between 3.5 to 4.0
printf("Students from KTM with CGPA between 3.5 and 4.0:\n");
printf("--------------------------------------------------\n");
int found_count = 0;
for (i = 0; i < 100; i++) {
// Check conditions: address is "KTM" AND CGPA is between 3.5 and 4.0 (inclusive)
if (strcmp(students[i].address, "KTM") == 0 && students[i].CGPA >= 3.5 && students[i].CGPA <= 4.0) {
printf("SID: %d\n", students[i].SID);
printf("Name: %s\n", students[i].name);
printf("Address: %s\n", students[i].address);
printf("CGPA: %.2f\n", students[i].CGPA);
printf("--------------------------------------------------\n");
found_count++;
}
}
if (found_count == 0) {
printf("No students found matching the criteria.\n");
}
return 0;
}
File opening modes dictate how a file is accessed and manipulated when opened. These modes specify operations like reading, writing, or appending, and determine the initial state of the file.
-
Read Mode ('r'):
- Opens a file for reading only.
- The file pointer is placed at the beginning of the file.
- If the file does not exist, an error is raised.
-
Write Mode ('w'):
- Opens a file for writing only.
- If the file exists, its content is truncated (emptied).
- If the file does not exist, a new file is created.
- The file pointer is placed at the beginning of the file.
-
Append Mode ('a'):
- Opens a file for writing, appending new data to the end of the file.
- If the file does not exist, a new file is created.
- The file pointer is placed at the end of the file.
-
Read and Write Mode ('r+'):
- Opens a file for both reading and writing.
- The file pointer is placed at the beginning of the file.
- If the file does not exist, an error is raised.
-
Write and Read Mode ('w+'):
- Opens a file for both writing and reading.
- If the file exists, its content is truncated.
- If the file does not exist, a new file is created.
- The file pointer is placed at the beginning of the file.
-
Binary Modes ('rb', 'wb', 'ab', 'r+b', etc.):
- Adding 'b' to any of the above modes opens the file in binary mode.
- Used for non-text files like images, audio, or executable files, where data is read/written as bytes without character encoding transformations.
import turtle
# 1. Initialize the graphics screen
screen = turtle.Screen()
screen.setup(width=600, height=400)
screen.title("Drawing Two Shapes")
# 2. Create a turtle object
pen = turtle.Turtle()
pen.speed(0) # Set drawing speed to fastest
# 3. Draw the first shape: A filled square
pen.penup() # Lift the pen
pen.goto(-150, 50) # Move to starting position
pen.pendown() # Put the pen down
pen.color("red") # Set pen color
pen.fillcolor("orange") # Set fill color
pen.begin_fill() # Start filling
for _ in range(4):
pen.forward(100)
pen.right(90)
pen.end_fill() # End filling
# 4. Draw the second shape: A filled circle
pen.penup() # Lift the pen
pen.goto(100, -50) # Move to starting position
pen.pendown() # Put the pen down
pen.color("blue") # Set pen color
pen.fillcolor("cyan") # Set fill color
pen.begin_fill() # Start filling
pen.circle(70) # Draw a circle with radius 70
pen.end_fill() # End filling
# 5. Keep the window open until closed by the user
screen.exitonclick()
Global Variable
- A variable declared outside any function or block, making it accessible from any part of the program.
- Its scope is global, meaning it can be read and modified by any function within the program.
- Lifetime persists throughout the entire execution of the program.
- Can be useful for sharing data across multiple functions or defining global constants.
- Excessive use can lead to issues such as naming conflicts, difficult-to-track modifications, and reduced code modularity and readability.
Debugging
- The systematic process of identifying, analyzing, and resolving errors (bugs) in computer software or hardware.
- Aims to ensure that a program functions correctly and as intended.
- Common techniques include:
- Breakpoints: Pausing program execution at specific lines of code.
- Step-through execution: Executing code line by line to observe its flow.
- Variable inspection: Examining the current values of variables at different points.
- Print statements (or logging): Inserting output statements to trace program flow and variable values.
- Utilizing integrated development environment (IDE) debuggers which provide tools for these techniques.
- Typically involves error detection, localization of the error, correction, and subsequent retesting.