Notes C programing (1)
Notes C programing (1)
C PROGRAMMING
Header files in C and C++ (.h or .hpp) act as blueprints, declaring functions, variables, data
structures, and macros without their implementation details. They separate the interface
(what something does) from the implementation (how it does it), promoting modularity and
code reusability across multiple source files.
By including header files, you inform the compiler about external elements, enabling the
linker to correctly connect different parts of your program. Include guards within header files
prevent errors from multiple inclusions. Ultimately, header files are essential for organized,
efficient, and maintainable C/C++ projects, streamlining compilation and preventing
redefinition issues.
Switch cases, while useful for multi-way branching, come with several limitations:
* Limited Data Types: They primarily work with integer types (char, int, enum) and constant
expressions. Floating-point numbers, strings, and complex objects cannot be directly used as
switch expressions or case labels.
* No Range Checking: You can't specify ranges within case labels. Each case must be a
single, distinct constant value.
* Fall-through Behavior (Often a Pitfall): Execution "falls through" to the next case if a break
statement is omitted. While sometimes intentional, it's a common source of bugs.
* Constant Case Labels: case labels must be constant expressions evaluated at compile time.
You cannot use variables or non-constant expressions.
* No Complex Conditions: Each case can only check for equality with a constant. You cannot
implement more complex logical conditions within a single case.
* Limited Scope for Case Labels: Case labels must be unique within a single switch block.
* Can Become Verbose: For a large number of discrete values, a switch statement can
become quite lengthy and less readable than other approaches.
Despite these limitations, switch statements remain a valuable tool when dealing with a
clear set of discrete integer-based choices.
The preprocessor is a program that runs before the actual compilation of your source code.
It manipulates the code based on directives you include in your source files, typically starting
with a # symbol. Think of it as a text substitution and code arrangement phase.
Usage in Programming:
* File Inclusion (#include): This is perhaps its most common use. #include directives insert
the entire contents of another file (usually a header file) into your current source file. This
allows you to access declarations of functions, variables, and data structures defined
elsewhere, promoting code reusability. For example, #include <stdio.h> brings in standard
input/output function declarations.
* Macro Definition (#define): #define allows you to create symbolic constants or short
aliases for code snippets.
* Constant Macros: #define PI 3.14159 replaces all occurrences of PI with 3.14159 before
compilation. This enhances readability and makes it easier to change values globally.
* Function-like Macros: #define SQUARE(x) ((x) * (x)) creates a shortcut for squaring a
value. However, be cautious as they involve textual substitution and can have unexpected
side effects.
* Conditional Compilation (#ifdef, #ifndef, #if, #else, #elif, #endif): These directives allow you
to selectively compile parts of your code based on whether certain conditions are true or
false. This is useful for:
* Platform-specific code: Compiling different code sections for different operating systems
or hardware.
* Debugging: Including debugging code during development and excluding it in the final
release.
* Other Directives: The preprocessor also handles directives like #undef (to undefine a
macro), #pragma (compiler-specific instructions), and #error (to generate a compilation
error with a custom message).
In essence, the preprocessor prepares your source code for the compiler by handling
inclusions, substitutions, and conditional compilation, making your code more flexible,
portable, and manageable.
C offers fundamental data types like int for whole numbers, char for single characters, float
and double for decimal numbers with varying precision. Derived types build upon these,
including pointers for memory addresses, arrays for collections of similar elements, struct
and union for grouping data, and Enum for named integer constants.
Qualifiers like signed, unsigned, short, long, const, and volatile modify the size, range, and
behaviour of basic types. Choosing the right data type is crucial for efficient memory usage
and accurate program execution. These types form the building blocks for representing
various kinds of data in C programs.
The scope of a variable refers to the region of the program where that variable can be
accessed (is "visible"). It determines where you can use the variable's name to refer to its
value. Common scopes include global (accessible throughout the program), local (accessible
only within a specific block like a function), and file scope (accessible only within the source
file).
The lifetime of a variable, on the other hand, defines how long a variable exists in memory
during the program's execution. Global and static local variables typically have a lifetime that
spans the entire program execution. Automatic local variables (the default inside functions)
exist only while the function or block of code where they are defined is being executed; they
are created when the block is entered and destroyed when it's exited. Dynamic variables,
allocated using functions like malloc, have a lifetime managed explicitly by the programmer
until they are deallocated using free.
In essence, scope is about visibility, while lifetime is about existence in memory. A variable
can be in scope but not yet have a valid value (e.g., before initialization), and its lifetime
dictates when the memory it occupies is allocated and deallocated.
Macro Expansion:
File Inclusion:
Macro Expansion: This is a preprocessor activity where identifiers (macros) defined using
#define are replaced by their corresponding values or code snippets throughout the source
code before compilation. This allows for symbolic constants and simple code substitutions,
but can sometimes lead to unexpected side effects if not used carefully due to its textual
nature.
File Inclusion: The #include preprocessor directive is used to insert the entire contents of a
specified file (usually a header file .h) into the current source file. This is crucial for accessing
declarations of functions, variables, and data structures defined in separate files or libraries,
promoting code reusability and modularity.
Enumerated data types: They're a neat way to make your code more readable and less
prone to errors.
Think of them as custom data types where you explicitly list all the possible values a variable
of that type can hold. Instead of using plain integers or strings to represent a set of related
constants, you give these constants meaningful names.
For example, if you're working with days of the week, instead of having to remember that 1
means Monday, 2 means Tuesday, and so on, you can define an enumerated type like:
enum Day {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY
};
This makes your code much clearer because today = WEDNESDAY is far more intuitive than
today = 3. Plus, the compiler can often perform better type checking, preventing you from
accidentally assigning an invalid value to a variable of an enumerated type.
Many programming languages like C, C++, Java, and Swift support enumerated types,
although the syntax might vary slightly. They are a small but powerful feature that can
significantly improve the clarity and maintainability of your code when dealing with a fixed
set of related values.
2 3 4 5 6 7
3 4 5 6 7
4 5 6 7
5 6 7
6 7
#include <stdio.h>
int main() {
return 0;
#include <stdio.h>
#include <stdlib.h>
int main() {
int n;
printf("Enter the size (n) of the number grids (e.g., 2 for a 2x2 grid): ");
scanf("%d", &n);
if (matrix1) free(matrix1);
if (matrix2) free(matrix2);
if (resultMatrix) free(resultMatrix);
return 1;
free(matrix1[j]);
free(matrix2[j]);
free(resultMatrix[j]);
free(matrix1);
free(matrix2);
free(resultMatrix);
return 1;
scanf("%d", &matrix1[i][j]);
scanf("%d", &matrix2[i][j]);
resultMatrix[i][j] = 0;
printf("\n");
free(matrix1[i]);
free(matrix2[i]);
free(resultMatrix[i]);
free(matrix1);
free(matrix2);
free(resultMatrix);
return 0;
* Asks for Size: The first printf and scanf do exactly that.
* Creates Empty Grids: The malloc parts are like setting up empty spaces in the computer's
memory to hold our number grids.
* Fills the First Grid: The nested for loops and scanf let you put numbers into the first grid.
* Fills the Second Grid: The next set of nested for loops and scanf does the same for the
second grid.
* Multiplies the Grids: The three nested for loops perform the special multiplication and
addition to get the numbers for the resultMatrix.
* Shows the Result: The final nested for loops and printf display the numbers in the
resultMatrix.
The code also includes some "good programmer" steps like checking if the computer had
enough memory to create the grids and cleaning up that memory at the end. This makes the
program more reliable.
Q9. Given an array of 20 integers. Write a program in C to search for a given integer in that
array.
#include <stdio.h>
int main() {
int arr[20];
int searchElement;
int found = 0;
int i;
scanf("%d", &arr[i]);
scanf("%d", &searchElement);
// If the element was not found after searching the entire array
if (!found) {
return 0;
Q10. Declare a structure which contains the following members and write a program in C
to list all students who scored more than 75 marks. Roll No., Name, Father's Name, Age,
City, Marks
#include <stdio.h>
#include <string.h>
struct Student {
int rollNumber;
char name[50];
char fathersName[50];
int age;
char city[50];
};
int main() {
int numStudents = 0;
// Let's add some sample student data (you would typically read this from a file or user
input)
printf("-----------------------------------------------------------------------------------\n");
printf("-----------------------------------------------------------------------------------\n");
printf("-----------------------------------------------------------------------------------\n");
return 0;
Think of your computer's memory as a vast storage area. Static memory allocation is like
reserving a fixed-size cabinet in advance, whether you fill it completely or not. Dynamic
memory allocation, however, is like having access to a flexible storage room where you can
request shelves (memory blocks) of varying sizes as needed during your work (program
execution) and return them when you're done.
This on-demand memory management is crucial for several reasons. Firstly, it offers
flexibility. You don't need to predict the exact memory requirements when you write your
code. Instead, you can adapt to runtime conditions, such as the size of a file the user opens
or the number of inputs they provide.
Secondly, it promotes efficiency. By allocating only the necessary memory, you avoid wasting
precious resources. Imagine reading a file; with dynamic allocation, you can allocate
memory to store the file's contents only after knowing its actual size, rather than pre-
allocating a potentially huge buffer that might remain mostly empty.
Finally, dynamic memory is the backbone of dynamic data structures. Structures like linked
lists, trees, and graphs can grow or shrink during program execution. This is only possible
through dynamic memory allocation, where you can allocate and deallocate memory for
individual nodes as needed.
C provides four essential functions for dynamic memory management, all found in the
stdlib.h library:
* malloc(): Asks for a specific number of bytes and returns a pointer to the beginning of an
uninitialized memory block in the heap. If the request fails (e.g., insufficient memory), it
returns NULL.
* calloc(): Similar to malloc, but it takes the number of elements and their size as arguments
and initializes the allocated memory to zero.
* realloc(): Attempts to resize a previously allocated memory block. It might expand, shrink,
or even move the block to a new location if necessary, copying the existing data. It's vital to
check the return value of realloc, as it might return NULL, leaving the original block
untouched.
* free(): Returns a dynamically allocated memory block back to the heap, making it available
for future use. Failing to free() allocated memory leads to memory leaks, a common
programming error that can cause programs to consume excessive memory and eventually
crash.
Q12. Explain different type of modes and I/O function in file handling.
You're asking about the different modes used when opening files in C and the standard
input/output (I/O) functions used in file handling. Let's break this down.
When you use the fopen() function to open a file in C, you need to specify a mode that
dictates how you intend to interact with the file. Here are the common file opening modes:
Basic Modes:
* "r" (Read):
* "w" (Write):
* "a" (Append):
* If the file exists, new data written to the file will be added at the end.
* The file pointer is positioned at the end of the file before each write operation.
* If the file exists, reading can start from the beginning, but writing always happens at the
end.
* The initial file pointer position for reading is at the beginning, but writing always
appends.
Binary Modes:
You can append "b" to any of the above modes to open a file in binary mode. This is crucial
for handling non-text files like images, audio, or executables, as it prevents any
interpretation or modification of the data based on character encodings.
* Existence of File: Be mindful of whether the file needs to exist beforehand or if the mode
will create it.
* Data Loss: "w" and "w+" will overwrite existing file content.
* Pointer Position: The initial position of the file pointer affects where reading and writing
begin.
* Text vs. Binary: Use binary modes for non-textual data to avoid corruption.
C provides a set of standard library functions (primarily in stdio.h) for performing I/O
operations on files. These functions allow you to read data from and write data to files.
Character I/O:
* fgetc(FILE *stream): Reads the next character from the specified input stream (file).
Returns the character read as an int (or EOF if the end of the file is reached or an error
occurs).
* fputc(int c, FILE *stream): Writes the character c to the specified output stream (file).
Returns the character written on success (as an int) or EOF on error.
Line I/O:
* fgets(char *s, int n, FILE *stream): Reads at most n-1 characters from the specified input
stream into the character array pointed to by s. It stops reading when a newline character is
encountered, the end of the file is reached, or n-1 characters have been read. The newline
character, if read, is included in the string. The string is null-terminated. Returns s on success
or NULL on error or end of file.
* fputs(const char *s, FILE *stream): Writes the null-terminated string s to the specified
output stream. It does not write the null terminator. Returns a non-negative value on
success or EOF on error.
Formatted I/O:
* fprintf(FILE *stream, const char *format, ...): Writes formatted output to the specified
output stream according to the format string. It works similarly to printf, but the output goes
to the file. Returns the number of characters written on success or a negative value on error.
* fscanf(FILE *stream, const char *format, ...): Reads formatted input from the specified
input stream according to the format string and stores the values in the variables pointed to
by the additional arguments. It works similarly to scanf, but the input comes from the file.
Returns the number of input items successfully matched and assigned, or EOF if the end of
the file is reached before any conversion.
* fread(void *ptr, size_t size, size_t count, FILE *stream): Reads up to count items of size
bytes each from the specified input stream into the memory location pointed to by ptr.
Returns the number of items successfully read (which might be less than count if an error or
end of file occurs).
* fwrite(const void *ptr, size_t size, size_t count, FILE *stream): Writes count items of size
bytes each from the memory location pointed to by ptr to the specified output stream.
Returns the number of items successfully written.
File Positioning:
* fseek(FILE *stream, long offset, int whence): Sets the file position indicator for the
specified stream.
* offset: The number of bytes to offset from the position specified by whence.
* ftell(FILE *stream): Returns the current file position indicator for the specified stream.
Returns a long representing the byte offset from the beginning of the file, or -1L on error.
* rewind(FILE *stream): Sets the file position indicator to the beginning of the specified
stream. It's equivalent to fseek(stream, 0, SEEK_SET) and also clears the error indicator.
* feof(FILE *stream): Checks if the end-of-file indicator for the specified stream is set.
Returns a non-zero value if it is set, and zero otherwise.
* ferror(FILE *stream): Checks if the error indicator for the specified stream is set. Returns a
non-zero value if an error has occurred, and zero otherwise.
* perror(const char *s): Prints an error message to the standard error stream (stderr),
preceded by the string s (if provided) and a colon and a space. The error message describes
the most recent error that occurred during a system call or library function call.
Understanding these file opening modes and I/O functions is fundamental to working with
files in C, allowing you to create programs that can read, write, and manipulate data stored
in external files. Remember to always close files using fclose(FILE *stream) when you are
finished with them to release system resources.
Q13. Define recursive function. Write a program to find factorial of a number with
recursive function.
A recursive function is a function that calls itself within its own definition. To avoid infinite
loops, a recursive function must have one or more base cases – conditions under which the
function stops calling itself and returns a value directly. Each recursive call should move
closer to one of these base cases. Recursive functions break down a problem into smaller,
self-similar subproblems, solving each subproblem by calling the same function again with a
modified input.
#include <stdio.h>
if (n == 0) {
return 1;
else if (n == 1) {
return 1;
return result;
else {
int main() {
int num;
scanf("%d", &num);
if (fact != -1) {
return 0;
}
Explanation of the Code:
* factorial(int n) Function:
* Base Cases:
* If n is 1, it returns 1 (since 1! = 1). These are the stopping conditions for the recursion.
* Recursive Step:
* If n is greater than 1, it calculates the factorial by multiplying n with the factorial of n-1.
This is where the function calls itself (factorial(n - 1)).
* The printf statements are added for tracing the function calls and returns, showing how
the calculation unfolds.
* If n is negative, it prints an error message and returns -1 to indicate that factorial is not
defined for negative numbers.
* main() Function:
* Calls the factorial() function to calculate the factorial of the entered number.
Let's say the user enters 5. Here's how the factorial(5) function will execute:
* factorial(5) is called.
* Calculates: 5 * factorial(4)
* factorial(4) is called.
* Calculates: 4 * factorial(3)
* factorial(3) is called.
* Prints: "Calculating factorial of 3..."
* Calculates: 3 * factorial(2)
* factorial(2) is called.
* Calculates: 2 * factorial(1)
* factorial(1) is called.
* Returns 1.
* Back in factorial(2):
* result becomes 2 * 1 = 2.
* Returns 2.
* Back in factorial(3):
* result becomes 3 * 2 = 6.
* Returns 6.
* Back in factorial(4):
* Returns 24.
* Back in factorial(5):
* Returns 120.
The tracing printf statements clearly show the "across the function" aspect, illustrating how
the function calls itself with a smaller input until it reaches the base case, and then the
results are returned back up the call stack to produce the final answer.
Q14. Write a program to check whether a given number is Armstrong number or not.
#include <stdio.h>
#include <math.h>
int main() {
double sum = 0;
scanf("%d", &num);
original = num;
while (original != 0) {
original /= 10;
digits++;
original = num;
while (original != 0) {
original /= 10;
}
if ((int)sum == num) {
} else {
return 0;
Q15. Write a program to copy a string to another string using standard library function. Use
dynamic memory allocation to accept string.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
size_t len = 0;
len = strlen(buffer);
strcpy(source, buffer);
dest = (char *)malloc((len + 1) * sizeof(char));
strcpy(dest, source);
free(source);
free(dest);
return 0;
} else {
printf("Input error.\n");
return 1;
Call by Value
* When you use "call by value", a copy of the actual argument's value is passed to the
function's formal parameter.
* The function creates a new local variable to hold this copied value.
* Any modifications made to the parameter inside the function do not affect the original
argument in the calling function.
* Think of it like giving a photocopy of a document to someone; they can write on their
copy, but your original remains unchanged.
* This method ensures the original data remains safe from accidental modifications within
the function.
* C uses call by value as its default mechanism for passing arguments.
#include <stdio.h>
void change(int x) {
int main() {
int a = 10;
change(a);
printf("Value after function call: %d\n", a); // Output: 10 (original 'a' is unchanged)
return 0;
Call by Reference
* When you use "call by reference", the memory address (or a reference to the memory
location) of the actual argument is passed to the function's formal parameter.
* The formal parameter acts as an alias or a pointer to the original argument in the calling
function.
* Any modifications made to the parameter inside the function directly affect the original
argument in the calling function because they are both referring to the same memory
location.
* Think of it like giving someone the actual original document; if they write on it, the
original is changed.
int main() {
int a = 10;
printf("Value after function call: %d\n", a); // Output: 100 (original 'a' is changed)
return 0;
|---|---|---|
| Passing | Copy of the variable's value | Memory address (pointer) of the variable |
| Formal Parameter | New local variable initialized with the copy | Pointer variable pointing
to the original |
| Changes Inside Function | Do not affect the original variable | Do affect the original
variable |
| Memory | Uses separate memory for argument and parameter | Both refer to the same
memory location |
| Use Case | When you don't want to modify the original | When you need to modify the
original |
Q16. What is a structure? How it is different from Union?
A structure in C groups different types of variables together, like a container holding various
items, each in its own space. You can access each item (member) independently using a dot.
A union, on the other hand, is like a single container that can hold different types of items,
but only one at a time. All members share the same memory space, so assigning a value to
one overwrites the others.
Structures are for holding distinct pieces of information, while unions are for saving memory
when you only need to use one of the members at any point.
* The size and location of memory are fixed before the program runs.
* Memory is automatically deallocated when the function exits or the program terminates.
* The size and location of memory can be determined during program execution.
* Used for data structures like linked lists, and when the memory requirement is not known
beforehand.
* Memory must be explicitly deallocated by the programmer using functions like free() to
prevent memory leaks.
* Size can be changed during program execution using functions like realloc().
Q18. What is the difference between getc(), getchar(), getch() and getche()?
between getchar(), gets(), getch(), and getche():
* Purpose: Reads a single character from the standard input (stdin), which is usually the
keyboard.
* Buffering: It is buffered. The character you type is not immediately processed until you
press the Enter key. If you type multiple characters before pressing Enter, they are stored in
the input buffer and read one by one by subsequent calls to getchar().
* Return Value: Returns the ASCII value of the character read as an int. Returns EOF (End-Of-
File) if an error occurs or the end of the input stream is reached.
* Purpose: Reads a line of text (characters until a newline character or EOF is encountered)
from the standard input (stdin) and stores it in a character array (string).
* Buffering: It is buffered.
* Echoing: The entered characters are echoed on the console. The newline character is read
but not stored in the string; instead, it is replaced by a null terminator (\0).
* Return Value: Returns a pointer to the character array (str) on success. Returns NULL if an
error occurs or if EOF is encountered before any characters are read.
* Security Risk: gets() is inherently unsafe and has been deprecated (and even removed in
later C standards). It does not perform bounds checking, meaning if the user enters a string
longer than the allocated size of the character array, it can lead to buffer overflows, which
are serious security vulnerabilities. Avoid using gets(). Use fgets() instead.
* Purpose: Reads a single character directly from the keyboard without echoing it to the
console and without requiring the Enter key to be pressed.
* Buffering: It is unbuffered or direct. As soon as you press a key, the character is read.
* Echoing: The entered character is not displayed on the screen. This makes it suitable for
reading passwords or other sensitive input.
* Return Value: Returns the ASCII value of the character entered as an int (though often cast
to char).
* Portability: conio.h is not part of the standard C library and is primarily associated with
older compilers like Turbo C/C++ and operating systems like MS-DOS. It might not be
available on other systems (like Linux or macOS) without specific emulation or libraries.
* Purpose: Reads a single character directly from the keyboard and echoes it to the console
immediately, without requiring the Enter key.
* Return Value: Returns the ASCII value of the character entered as an int (though often cast
to char).
* Portability: Like getch(), getche() is also part of the non-standard conio.h header and has
limited portability.
In summary:
|---|---|---|---|---|---|---|---|---|
| getchar() | <stdio.h> | Single char | Yes | Yes | Yes | Yes | Portable | General single
character input. |
| gets() | <stdio.h> | Line | Yes | Yes | Yes | Yes (but deprecated) | Less portable, unsafe |
Reading a line of text (but avoid using). |
For modern, portable C code, prefer getchar() for single characters (and handle buffering if
needed) and fgets() for reading lines of text as it provides buffer overflow protection. Avoid
gets(). Use getch() and getche() with caution, understanding their non-standard nature and
limited portability.
Q19. Write a C program that reads 20 integer numbers from keywords and sort them in
ascending order.
#include <stdio.h>
int main() {
printf("Enter 20 integers:\n");
scanf("%d", &nums[i]);
temp = nums[j];
nums[j + 1] = temp;
printf("\nSorted (ascending):\n");
printf("\n");
return 0;
}
Q20. Write a C program to check input string is palindrome or not without using any string
library function.
#include <stdio.h>
#include <stdbool.h>
int len = 0;
return len;
return true;
int main() {
char str[100];
scanf("%s", str);
if (isPal(str)) {
printf("\"%s\" is a palindrome.\n", str);
} else {
return 0;
Q21. Write a C program to add two complex numbers by passing structure to a function.
#include <stdio.h>
struct Complex {
double real;
double imag;
};
return sum;
int main() {
printf("Enter the real and imaginary parts of the first complex number: ");
printf("Enter the real and imaginary parts of the second complex number: ");
return 0;
#include <stdio.h>
int main() {
int arr[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
printf("\n");
return 0;
Q24. How string is different an array? Write a C program convert a lower case string to
upper case string without using string manipulation function.
Unlike many other programming languages that treat strings as a built-in data type with
associated functions (like Python, Java, etc.), C handles strings quite differently:
* Character Arrays: In C, a string is essentially an array of characters. This means you need to
allocate a contiguous block of memory to store the sequence of characters that make up the
string.
* Null Termination: C strings are null-terminated. This means that the end of a string is
marked by a special character called the null character (\0). This null terminator is crucial
because it's how C functions that work with strings (like printf with %s) know where the
string ends. Without it, these functions would keep reading memory until they encounter a
null byte by chance, leading to unpredictable behavior and potential crashes.
* No Built-in String Type: C doesn't have a dedicated string keyword as a fundamental data
type. You work directly with char arrays.
* Manual Memory Management: Because strings are arrays, you often need to handle
memory allocation and deallocation yourself, especially if you're creating strings dynamically.
* String Manipulation Functions: C provides a set of functions in the string.h header file (like
strcpy, strcat, strlen, strcmp, etc.) for performing common string operations. However, as
you requested, we'll avoid using these for our conversion program to understand the
underlying mechanics.
Here's a C program that converts a lowercase string to uppercase without relying on the
functions from string.h:
#include <stdio.h>
#include <ctype.h> // For the toupper function (we'll use this for comparison, but avoid
string.h)
int i = 0;
i++;
int main() {
toUpperCase(text);
printf("Uppercase string: %s\n", text);
return 0;
Q25. Write a program in C to count the number of words and characters in an input file
test 1.txt.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int main() {
FILE *file;
char ch;
int wordCount = 0;
int charCount = 0;
int inWord = 0;
if (file == NULL) {
return EXIT_FAILURE;
if (isspace(ch)) {
inWord = 0;
} else if (!inWord) {
inWord = 1;
wordCount++;
fclose(file);
return 0;
Q26. Write a program in C for the multiplication of two matrices of size 4x4.
#include <stdio.h>
#include <stdlib.h>
#define SIZE 4
result[i][j] = 0;
for (int k = 0; k < SIZE; k++) {
printf("%d\t", matrix[i][j]);
printf("\n");
int main() {
int matrix1[SIZE][SIZE] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
};
int matrix2[SIZE][SIZE] = {
{8, 7, 6, 5},
{4, 3, 2, 1}
};
int product[SIZE][SIZE];
printf("Matrix 1:\n");
printMatrix(matrix1);
printf("\nMatrix 2:\n");
printMatrix(matrix2);
printMatrix(product);
return 0;
Q27. Create a structure named 'Book' to store book details like title, author, and price.
Write a C program to input details for three books, find the most expensive and the lowest
priced books, and display their information.
#include <stdio.h>
#include <string.h>
#define NUM_BOOKS 3
// Structure to store book details
struct Book {
char title[MAX_TITLE_AUTHOR_LENGTH];
char author[MAX_TITLE_AUTHOR_LENGTH];
float price;
};
int main() {
printf("Title: ");
books[i].title[strcspn(books[i].title, "\n")] = 0;
printf("Author: ");
books[i].author[strcspn(books[i].author, "\n")] = 0;
printf("Price: ");
scanf("%f", &books[i].price);
maxPrice = books[i].price;
mostExpensiveIndex = i;
minPrice = books[i].price;
lowestPriceIndex = i;