C Programming
Deep Understanding: 70 hours
Community
C Programming
2080 Boards
Section A
Answer any two questions.
Structure
A structure (often referred to as a struct in C/C++) is a user-defined data type that allows grouping logically related data items of different data types under a single name. It acts as a blueprint for creating variables that can hold multiple pieces of information. Structures are useful for representing real-world entities that have various attributes, such as a student (name, roll number, marks) or a date (day, month, year).
Key characteristics:
- User-defined: Programmers define its members and their types.
- Heterogeneous: Can contain members of different data types (e.g.,
int,float,char[]). - Memory allocation: Memory for all members is allocated contiguously when a structure variable is declared.
- Member access: Individual members are accessed using the dot (
.) operator with a structure variable, or the arrow (->) operator with a pointer to a structure.
Example Syntax:
struct Student {
char name[50];
int rollNumber;
float marks;
};
Nested Structure
A nested structure is a structure defined within another structure. This allows for a more hierarchical and organized grouping of related data. It is particularly useful when certain attributes of an entity can themselves be represented as a complex type with multiple sub-attributes.
For example, if a Student structure needs to store DateOfBirth, and DateOfBirth itself consists of day, month, and year, then DateOfBirth can be defined as a separate structure and then embedded within the Student structure.
Example Syntax:
struct Date {
int day;
int month;
int year;
};
struct Student {
char name[50];
int rollNumber;
struct Date dateOfBirth; // Nested structure member
float marks;
};
Accessing members of a nested structure requires using multiple dot operators, e.g., student1.dateOfBirth.year.
Program to find out whether the nth term of the Fibonacci series is a prime number or not
#include <stdio.h>
#include <stdbool.h> // For bool data type
#include <math.h> // For sqrt()
// Function to generate the nth Fibonacci term
long long generateNthFibonacci(int n) {
if (n <= 0) {
return 0; // Or handle as an error, depending on definition
} else if (n == 1) {
return 0; // F(1) typically 0 in 0, 1, 1, 2... sequence
} else if (n == 2) {
return 1; // F(2) typically 1
}
long long a = 0;
long long b = 1;
long long nextTerm;
// Loop calculates Fibonacci from F(3) up to F(n)
for (int i = 3; i <= n; i++) {
nextTerm = a + b;
a = b;
b = nextTerm;
}
return b;
}
// Function to check if a number is prime
bool isPrime(long long num) {
if (num <= 1) {
return false;
}
if (num == 2) {
return true;
}
if (num % 2 == 0) { // All even numbers > 2 are not prime
return false;
}
// Check for odd divisors from 3 up to sqrt(num)
for (long long i = 3; i * i <= num; i += 2) {
if (num % i == 0) {
return false;
}
}
return true;
}
// Main function to read input and display result
int main() {
int n;
long long fibTerm;
bool primeResult;
printf("Enter the value of n (for the nth Fibonacci term): ");
scanf("%d", &n);
if (n < 1) {
printf("Please enter a positive integer for n.\n");
return 1; // Indicate error
}
// Generate the nth Fibonacci term
fibTerm = generateNthFibonacci(n);
// Check if the Fibonacci term is prime
primeResult = isPrime(fibTerm);
// Display the result
printf("The %dth Fibonacci term is: %lld\n", n, fibTerm);
if (primeResult) {
printf("%lld is a prime number.\n", fibTerm);
} else {
printf("%lld is NOT a prime number.\n", fibTerm);
}
return 0;
}
Relation between Array and Pointer
Arrays and pointers are closely related in C/C++, often used interchangeably in certain contexts, though they possess fundamental differences.
- Array Name as a Pointer: In C/C++, an array name, when used without an index, decays into a constant pointer to its first element. For example,
int arr[5];meansarrcan be treated as&arr[0]. - Memory Contiguity: Arrays allocate a contiguous block of memory for all their elements. Pointers, conversely, can point to any memory location, whether contiguous or not (though array pointers will point to contiguous blocks).
- Pointer Arithmetic: Pointer arithmetic is fundamental to both. If
ptrpoints to the first element of an array, then*(ptr + i)is equivalent toptr[i]and accesses thei-th element (0-indexed). This showcases how array indexing is internally handled using pointer arithmetic. - Function Parameters: When an array is passed to a function, it is effectively passed as a pointer to its first element. This means the function receives the memory address of the first element, allowing it to access and potentially modify the original array elements.
- Key Distinction:
- An array name is a constant pointer; its value (the base address) cannot be changed. For instance,
arr = another_address;is invalid. - A pointer variable is a variable that stores an address; its value can be changed to point to different memory locations. For example,
int *ptr; ptr = &some_variable; ptr = arr;are all valid. sizeof(array_name)gives the total size of the array in bytes, whilesizeof(pointer_name)gives the size of the pointer itself (e.g., 4 or 8 bytes), regardless of what it points to.
- An array name is a constant pointer; its value (the base address) cannot be changed. For instance,
Differentiate Call by Value and Call by Reference
Call by Value
- Mechanism: When a function is called using call by value, copies of the actual parameters' values are passed to the formal parameters of the function.
- Data Flow: The function works on these copies, not on the original variables.
- Impact on Original Data: Any modifications made to the formal parameters inside the function do not affect the original actual parameters in the calling scope.
- Memory: Actual and formal parameters occupy distinct memory locations.
- Use Case: Suitable when the function only needs to read the values and should not alter the original data.
Call by Reference
- Mechanism: When a function is called using call by reference, the memory addresses (or references) of the actual parameters are passed to the formal parameters.
- Data Flow: The function directly accesses and works with the original variables through their memory addresses.
- Impact on Original Data: Any modifications made to the formal parameters inside the function directly affect the original actual parameters in the calling scope.
- Memory: Actual and formal parameters refer to the same memory locations.
- Use Case: Suitable when the function needs to modify the original data, or when passing large data structures to avoid costly copying.
Program Example (C/C++)
#include <stdio.h>
// Function demonstrating Call by Value
void swapByValue(int x, int y) {
int temp = x;
x = y;
y = temp;
printf("Inside swapByValue function: x = %d, y = %d\n", x, y);
}
// Function demonstrating Call by Reference (using pointers)
void swapByReference(int *x, int *y) {
int temp = *x;
*x = *y;
*y = temp;
printf("Inside swapByReference function: *x = %d, *y = %d\n", *x, *y);
}
int main() {
int a = 10, b = 20;
printf("--- Demonstrating Call by Value ---\n");
printf("Before swapByValue call: a = %d, b = %d\n", a, b);
swapByValue(a, b); // Passing copies of a and b
printf("After swapByValue call: a = %d, b = %d\n\n", a, b); // a and b remain unchanged
int p = 30, q = 40;
printf("--- Demonstrating Call by Reference ---\n");
printf("Before swapByReference call: p = %d, q = %d\n", p, q);
swapByReference(&p, &q); // Passing addresses of p and q
printf("After swapByReference call: p = %d, q = %d\n", p, q); // p and q are modified
return 0;
}
Output of the program:
--- Demonstrating Call by Value ---
Before swapByValue call: a = 10, b = 20
Inside swapByValue function: x = 20, y = 10
After swapByValue call: a = 10, b = 20
--- Demonstrating Call by Reference ---
Before swapByReference call: p = 30, q = 40
Inside swapByReference function: *x = 40, *y = 30
After swapByReference call: p = 40, q = 30
Source Code vs. Object Code Differentiation
Source code and object code represent different stages in the software development process, particularly in compiled languages.
-
Source Code:
- Definition: Source code is a high-level programming language code written by a programmer that is human-readable and understandable. It consists of instructions, statements, and declarations following the syntax rules of a specific programming language (e.g., C, C++, Java, Python).
- Format: Typically plain text files with specific file extensions (e.g.,
.c,.cpp,.java). - Creation: Written by software developers using text editors or Integrated Development Environments (IDEs).
- Executability: Cannot be directly executed by the computer's CPU. It must first be translated into machine-executable form.
- Purpose: Primarily for development, human understanding, modification, and maintenance of software.
- Portability: Generally more portable across different hardware architectures and operating systems (with recompilation).
-
Object Code:
- Definition: Object code is the machine-level code generated by a compiler or assembler from source code. It is an intermediate representation, largely unreadable by humans, consisting of machine instructions and data in a binary format.
- Format: Binary files with specific file extensions (e.g.,
.objon Windows,.oon Unix-like systems). It contains machine instructions, relocation information, and symbols. - Creation: Generated automatically by a compiler (from high-level language source code) or an assembler (from assembly language source code).
- Executability: Not directly executable on its own; it typically needs to be linked with other object files and libraries by a linker to form a complete executable program.
- Purpose: To provide a low-level, machine-understandable translation of the source code, ready for the linking phase.
- Portability: Highly machine-specific and processor-architecture dependent. Object code generated for one architecture (e.g., x86) will not run on another (e.g., ARM).
Book Structure and Data Processing
#include <stdio.h>
#include <string.h> // Required for string operations if needed, though not strictly for this exact problem
// Structure definition for Book
struct Book {
char Book_Name[50];
float Price;
char Author_Name[50];
};
int main() {
// Declare an array of 10 Book structures
struct Book library[10];
int i;
printf("Enter details for 10 books:\n");
// Take input for 10 records of Book
for (i = 0; i < 10; i++) {
printf("\nBook %d:\n", i + 1);
printf(" Enter Book Name: ");
// Using scanf for strings, handle potential issues with spaces for simplicity in exam context
scanf("%s", library[i].Book_Name);
printf(" Enter Price: ");
scanf("%f", &library[i].Price);
printf(" Enter Author Name: ");
scanf("%s", library[i].Author_Name);
}
printf("\nAuthors of books with price greater than 1000:\n");
// Iterate through records and print author names for books with price > 1000
for (i = 0; i < 10; i++) {
if (library[i].Price > 1000.0) {
printf("- %s\n", library[i].Author_Name);
}
}
return 0;
}
Section B
Answer any two questions.
Different types of I/O functions used in file handling:
-
File Opening Function:
- Description: Used to establish a connection between a program and a file on storage, returning a file pointer (handle) for subsequent operations. It specifies the file name and the mode of access (read, write, append, etc.).
- Syntax (C-like):
FILE *fopen(const char *filename, const char *mode);filename: Path to the file.mode: Access mode (e.g., "r" for read, "w" for write, "a" for append, "rb" for binary read).
-
File Reading Functions:
- Description: Used to retrieve data from an opened file into a program's memory. Types include reading character by character, line by line, formatted data, or blocks of binary data.
- Syntax (C-like examples):
- Character:
int fgetc(FILE *stream); - Line:
char *fgets(char *s, int n, FILE *stream); - Formatted:
int fscanf(FILE *stream, const char *format, ...); - Block:
size_t fread(void *ptr, size_t size, size_t count, FILE *stream);ptr: Pointer to the buffer where data is stored.size: Size of each data item to read.count: Maximum number of items to read.stream: File pointer.
- Character:
-
File Writing Functions:
- Description: Used to transfer data from a program's memory to an opened file. Similar to reading, functions exist for writing characters, strings, formatted data, or blocks of binary data.
- Syntax (C-like examples):
- Character:
int fputc(int char_to_write, FILE *stream); - String:
int fputs(const char *s, FILE *stream); - Formatted:
int fprintf(FILE *stream, const char *format, ...); - Block:
size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);ptr: Pointer to the data to be written.size: Size of each data item to write.count: Number of items to write.stream: File pointer.
- Character:
-
File Positioning Functions:
- Description: Used to change the current read/write position (offset) within an open file. This allows random access to different parts of the file.
- Syntax (C-like):
int fseek(FILE *stream, long offset, int origin);stream: File pointer.offset: Number of bytes to move.origin: Starting point for offset (e.g.,SEEK_SETfor beginning,SEEK_CURfor current position,SEEK_ENDfor end of file).
-
File Closing Function:
- Description: Used to terminate the connection between a program and an opened file, releasing system resources and ensuring all buffered data is written to disk.
- Syntax (C-like):
int fclose(FILE *stream);stream: File pointer.
def find_largest_in_rows():
"""
Reads a PxQ matrix of integers and displays the largest integer of each row.
"""
# 1. Read matrix dimensions P and Q
try:
P = int(input("Enter the number of rows (P): "))
Q = int(input("Enter the number of columns (Q): "))
except ValueError:
print("Invalid input. Please enter integers for P and Q.")
return
if P <= 0 or Q <= 0:
print("P and Q must be positive integers.")
return
matrix = []
print(f"Enter {P*Q} integers for the {P}x{Q} matrix:")
# 2. Read matrix elements
for i in range(P):
row = []
print(f"Enter {Q} integers for row {i+1}, separated by spaces:")
try:
elements = list(map(int, input().split()))
if len(elements) != Q:
print(f"Error: Expected {Q} integers for row {i+1}, but got {len(elements)}. Please re-enter.")
# Re-do the current row if input count is wrong
i -= 1
continue
row.extend(elements)
matrix.append(row)
except ValueError:
print("Invalid input. Please enter integers only.")
# Re-do the current row if non-integer input
i -= 1
continue
# 3. Find and display the largest integer of each row
print("\nLargest integer in each row:")
for i, row in enumerate(matrix):
if row: # Ensure the row is not empty
largest_in_row = max(row)
print(f"Row {i+1}: {largest_in_row}")
else:
print(f"Row {i+1} is empty.")
# Call the function to run the program
find_largest_in_rows()
def factorial(n):
"""
Calculates the factorial of a non-negative integer using recursion.
"""
if n < 0:
return "Factorial is not defined for negative numbers."
elif n == 0 or n == 1:
return 1
else:
return n * factorial(n - 1)
# Example usage:
if __name__ == "__main__":
try:
num = int(input("Enter a non-negative integer: "))
result = factorial(num)
print(f"The factorial of {num} is {result}")
except ValueError:
print("Invalid input. Please enter an integer.")
def check_palindrome():
word = input("Enter a word: ")
processed_word = word.lower()
reversed_word = processed_word[::-1]
if processed_word == reversed_word:
print(f"'{word}' is a palindrome.")
else:
print(f"'{word}' is not a palindrome.")
if __name__ == "__main__":
check_palindrome()
Types of operators:
-
Arithmetic Operators:
- Perform mathematical calculations like addition, subtraction, multiplication, division, and modulus.
- Examples:
+,-,*,/,%. - Expression:
result = a + b;
-
Relational (Comparison) Operators:
- Used to compare two operands and determine the relationship between them.
- Return a boolean value (true or false).
- Examples:
==(equal to),!=(not equal to),>(greater than),<(less than),>=(greater than or equal to),<=(less than or equal to). - Expression:
if (x > y)
-
Logical Operators:
- Combine or negate boolean expressions to produce a single boolean result.
- Examples:
&&(logical AND),||(logical OR),!(logical NOT). - Expression:
if (age > 18 && hasLicense)
Program Trace:
-
Initialization:
iis initialized to0.kis declared.
-
Loop Execution (
for(k=5; k>=0; k--)):- k = 5:
i = i + kbecomesi = 0 + 5→i = 5. - k = 4:
i = i + kbecomesi = 5 + 4→i = 9. - k = 3:
i = i + kbecomesi = 9 + 3→i = 12. - k = 2:
i = i + kbecomesi = 12 + 2→i = 14. - k = 1:
i = i + kbecomesi = 14 + 1→i = 15. - k = 0:
i = i + kbecomesi = 15 + 0→i = 15. - k = -1: The loop condition
k >= 0(-1 >= 0) is false. Loop terminates.
- k = 5:
-
Print Statement:
printf("%d\t", i);prints the final value ofi.
Output:
15
#include<stdio.h>
int calculateSum(int num, int count, int sum){
if(count!=0){
return calculateSum(num+2,count-1,sum+num);
}
return sum;
}
int main(){
printf("The sum of first 10 even number is: %d",calculateSum(2,12,0));
return 0;
}
Dynamic memory allocation is the process of allocating memory during program execution (runtime) from the heap segment of memory, rather than at compile time or upon function call (stack). This allows programs to handle variable amounts of data whose size is not known until the program is running.
Key characteristics:
- Runtime allocation: Memory is requested and allocated while the program is actively executing.
- Heap memory: The allocated memory resides in the heap, a large pool of free memory managed by the operating system.
- Flexibility: It enables programs to create data structures (like arrays or lists) whose size can change dynamically based on user input or other runtime conditions.
- Manual management: Programmers are responsible for both allocating and deallocating (freeing) dynamically allocated memory. Failure to deallocate leads to memory leaks.
- Pointers: Memory is accessed via pointers returned by allocation functions.
Program Example (C Language):
This program demonstrates allocating an integer array of user-specified size at runtime using malloc and then deallocating it with free.
#include <stdio.h>
#include <stdlib.h> // Required for malloc and free
int main() {
int *arr; // Pointer to hold the base address of the allocated memory
int n, i;
printf("Enter the number of elements: ");
scanf("%d", &n);
// Allocate memory for 'n' integers using malloc
// malloc returns a void pointer, which is then cast to int*
// If allocation fails, malloc returns NULL
arr = (int *) malloc(n * sizeof(int));
if (arr == NULL) {
printf("Memory allocation failed!\n");
return 1; // Indicate an error
}
printf("Enter %d integers:\n", n);
for (i = 0; i < n; i++) {
scanf("%d", &arr[i]); // Store values in the allocated memory
}
printf("Elements entered are: ");
for (i = 0; i < n; i++) {
printf("%d ", arr[i]); // Access values
}
printf("\n");
// Free the allocated memory to prevent memory leaks
free(arr);
printf("Memory freed successfully.\n");
return 0; // Indicate successful execution
}
# Initialize an array of dimension 10
# Example values are used for demonstration. In a real scenario, these could be user inputs or generated.
numbers = [64, 34, 25, 12, 22, 11, 90, 8, 45, 77]
# Sort the numbers within the array in ascending order
numbers.sort()
# Print the sorted array
print("Sorted array (ascending order):", numbers)