1. Functions in C
A function is a block of code that performs a specific task. By dividing a complex program into smaller, manageable functions, it becomes easier to understand, debug, and maintain. Functions are a core part of modular programming in C.
Introduction and Syntax
A function in C consists of a declaration (prototype), a definition, and a call. It takes input (arguments), processes it, and returns an output.
// Function Prototype (Declaration)
returnType functionName(dataType parameter1, dataType parameter2, ...);
// Function Definition
returnType functionName(dataType parameter1, dataType parameter2, ...) {
// block of code to be executed
...
return value; // Optional
}
// Function Call
result = functionName(argument1, argument2, ...);
Purpose and Advantages of Functions
- Reusability: A function can be called multiple times from different parts of the program, reducing code duplication.
- Modularity: Breaks down a large program into smaller, independent modules, making the code easier to manage.
- Readability: Improves the clarity and organization of the code.
- Debugging: It is easier to identify and fix errors in smaller, isolated functions.
Components of a Function
- Function Prototype (Declaration): Informs the compiler about a function's name, return type, and parameters. It must be declared before the function is called.
- Function Definition: Contains the actual block of code for the function's task.
- Function Call: The statement that executes the function's code.
- Return Statement: Used to send a value back from the function to the calling part of the program.
Types of Functions
Functions can be broadly categorized into two types:
- Library Functions: Pre-defined functions available in C's standard library (e.g.,
printf()
,scanf()
,sqrt()
). You just need to include the appropriate header file. - User-Defined Functions: Functions created by the programmer to perform specific tasks.
Passing Arguments
Arguments can be passed to functions in two main ways:
- Call By Value: A copy of the argument's value is passed to the function. Any changes made to the parameter inside the function do not affect the original variable.
- Call By Reference: The address of the argument is passed. The function can then directly access and modify the original variable using this address.
Variable and its Scope
The scope of a variable determines where it can be accessed within a program.
- Local Variables: Declared inside a function or a block. They can only be accessed within that function or block.
- Global Variables: Declared outside all functions. They can be accessed from any function in the program.
Storage Classes
Storage classes determine the scope, lifetime, and storage location of variables.
- Automatic (
auto
): Default storage class for local variables. Stored on the stack. Life is limited to the function block. - External (
extern
): Used to declare a global variable that is defined in another file. It extends the variable's visibility. - Static (
static
): For local variables, it retains the value between function calls. For global variables, it limits visibility to the current file. - Register (
register
): Suggests to the compiler to store the variable in a CPU register for faster access.
Function with Array Example
To pass an array to a function, you pass the array's name, which decays into a pointer to its first element. The function can then access and modify the array's elements.
#include <stdio.h>
void printArray(int arr[], int size) {
printf("Elements of array: ");
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int myArray[] = {10, 20, 30, 40, 50};
int n = sizeof(myArray) / sizeof(myArray[0]);
printArray(myArray, n);
return 0;
}
Recursive Function
A recursive function is a function that calls itself. It is used to solve problems that can be broken down into smaller, self-similar sub-problems.
Syntax:
void recursiveFunction() {
if (condition) {
// base case to stop recursion
return;
}
recursiveFunction(); // recursive call
}
Example: Factorial Calculation
#include <stdio.h>
long long factorial(int n) {
// Base case: to stop the recursion
if (n >= 1) {
return n * factorial(n - 1);
} else {
return 1;
}
}
int main() {
int number = 5;
printf("Factorial of %d = %lld\n", number, factorial(number));
return 0;
}
Advantages and Disadvantages of Recursion:
- Advantages:
- Code is often more elegant and shorter.
- Reduces code size and improves readability.
- Suitable for problems with a recursive nature (e.g., tree traversals, quicksort).
- Disadvantages:
- Can be slower due to function call overhead.
- Uses more memory (stack space).
- Difficult to debug.
2. Structure and Union
Structures and Unions are user-defined data types in C that allow you to combine different data types under a single name.
Structure (struct
)
A structure is a collection of variables of different data types under a single name. Each member in a structure has its own memory location.
Introduction and Syntax:
// Defining a structure
struct Student {
char name[50];
int rollNumber;
float marks;
};
Structure Size and Accessing Members:
The size of a structure is the sum of the sizes of all its members, plus any padding added by the compiler. You can access members using the dot (.
) operator.
#include <stdio.h>
#include <string.h>
struct Student {
char name[50];
int rollNumber;
float marks;
};
int main() {
struct Student s1;
strcpy(s1.name, "Alice");
s1.rollNumber = 101;
s1.marks = 85.5;
printf("Student Name: %s\n", s1.name);
printf("Roll Number: %d\n", s1.rollNumber);
printf("Marks: %.2f\n", s1.marks);
printf("Size of Student structure: %lu bytes\n", sizeof(struct Student));
return 0;
}
Nested Structure
A nested structure is a structure that contains another structure as one of its members.
#include <stdio.h>
#include <string.h>
struct DateOfBirth {
int day;
int month;
int year;
};
struct Student {
char name[50];
int rollNumber;
struct DateOfBirth dob; // Nested structure
};
int main() {
struct Student s1;
strcpy(s1.name, "Bob");
s1.rollNumber = 102;
s1.dob.day = 15;
s1.dob.month = 6;
s1.dob.year = 2005;
printf("Student Name: %s\n", s1.name);
printf("Date of Birth: %d-%d-%d\n", s1.dob.day, s1.dob.month, s1.dob.year);
return 0;
}
Array of Structure
An array of structures allows you to store multiple records of the same structure type.
#include <stdio.h>
struct Student {
char name[50];
int rollNumber;
};
int main() {
struct Student class[3]; // Array of 3 Student structures
class[0].rollNumber = 101;
strcpy(class[0].name, "Alice");
class[1].rollNumber = 102;
strcpy(class[1].name, "Bob");
class[2].rollNumber = 103;
strcpy(class[2].name, "Charlie");
for (int i = 0; i < 3; i++) {
printf("Student %d: %s (Roll No: %d)\n", i+1, class[i].name, class[i].rollNumber);
}
return 0;
}
Passing Structure to Function
You can pass an entire structure to a function, either by value or by reference (using pointers).
#include <stdio.h>
struct Point {
int x;
int y;
};
// Pass by value
void printPoint(struct Point p) {
printf("Point coordinates (pass by value): (%d, %d)\n", p.x, p.y);
}
// Pass by reference
void modifyPoint(struct Point *p) {
p->x = 20;
p->y = 40;
}
int main() {
struct Point p1 = {10, 20};
printPoint(p1);
modifyPoint(&p1);
printf("Modified point coordinates (pass by reference): (%d, %d)\n", p1.x, p1.y);
return 0;
}
Union (union
)
A union is a special data type that allows different members to share the **same memory location**. The size of a union is determined by its largest member.
Introduction and Syntax:
// Defining a union
union Data {
int i;
float f;
char str[20];
};
Comparison between Structure and Union
Feature | Structure | Union |
---|---|---|
Memory Allocation | Each member has its own unique memory location. | All members share the same memory location. |
Size | Sum of the sizes of all its members (plus padding). | Equal to the size of the largest member. |
Value Storage | All members can store values simultaneously. | Only one member can store a value at a time. The last assigned value overwrites previous ones. |
Keyword | struct |
union |
3. Pointers
A pointer is a variable that stores the memory address of another variable. It is a fundamental and powerful concept in C.
Introduction and Syntax:
Pointers are declared using an asterisk (*
).
dataType *pointerName;
The address of a variable is obtained using the address-of operator (&
), and the value at an address is accessed using the dereference operator (*
).
Concept of Value and Address
Every variable has a value and a memory address. The value is the data stored in that memory location, and the address is the location itself. A pointer holds an address as its value.
Declaration and Initialization:
#include <stdio.h>
int main() {
int var = 10;
int *ptr; // Pointer declaration
ptr = &var; // Pointer initialization (stores address of var)
printf("Value of var: %d\n", var);
printf("Address of var: %p\n", &var);
printf("Value stored in ptr (address): %p\n", ptr);
printf("Value pointed to by ptr: %d\n", *ptr);
return 0;
}
Pointer and Function: Call by Reference
Pointers are essential for implementing "call by reference," allowing a function to modify the actual variables in the calling function. The address is passed to the function, and the pointer inside the function can dereference it to modify the original value.
#include <stdio.h>
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 10, y = 20;
printf("Before swap: x = %d, y = %d\n", x, y);
swap(&x, &y); // Pass addresses
printf("After swap: x = %d, y = %d\n", x, y);
return 0;
}
Comparison between Call by Value and Call by Reference:
Aspect | Call by Value | Call by Reference |
---|---|---|
What is Passed | A copy of the argument's value. | The memory address of the argument. |
Modification | Changes inside the function do not affect the original variable. | Changes inside the function do affect the original variable. |
Memory | Separate memory is allocated for the parameter. | The same memory location is used for the variable. |
Use Case | When you want to protect the original data from modification. | When you need to modify the original data. |
Pointer with Arrays
An array's name acts as a constant pointer to its first element. This means you can use pointers to access and traverse array elements.
#include <stdio.h>
int main() {
int arr[] = {10, 20, 30, 40, 50};
int *ptr = arr; // ptr points to the first element (arr[0])
// Accessing elements using pointer arithmetic
printf("Value of arr[0]: %d\n", *ptr);
printf("Value of arr[1]: %d\n", *(ptr + 1));
printf("Value of arr[2]: %d\n", *(ptr + 2));
// Traversing the array
printf("\nTraversing array with pointer:\n");
for(int i = 0; i < 5; i++) {
printf("%d ", *(ptr + i));
}
printf("\n");
return 0;
}
Advantages and Disadvantages of Pointers:
- Advantages:
- Efficient memory management and dynamic memory allocation.
- Reduces code size and improves performance for large data sets.
- Allows for passing variables by reference to functions.
- Disadvantages:
- Can lead to memory leaks and dangling pointers if not managed correctly.
- Complex to understand for beginners.
- Unsafe if not handled carefully.
4. File Handling
File handling in C allows a program to store and retrieve data from files on disk, ensuring data persistence even after the program terminates.
Concept of Data File and Need for File Handling in C
A data file is a collection of related records stored on a physical storage device. File handling is necessary for managing data that needs to be stored permanently and shared between different program executions.
Files can be categorized as **Sequential** (accessed in a sequence from beginning to end) or **Random** (accessed directly at any location). Most of the file handling functions are for sequential access.
File Handling Functions
These are standard library functions for performing file operations. All of them use a file pointer (FILE*
).
FILE *fopen(const char *filename, const char *mode);
: Opens a file and returns a file pointer. ReturnsNULL
on failure.filename
: The name of the file to open.mode
: The file opening mode (e.g., "r", "w", "a").
int fclose(FILE *fp);
: Closes an opened file and flushes its buffer.fp
: The file pointer of the file to close.
int getc(FILE *fp);
: Reads a single character from the file.int putc(int c, FILE *fp);
: Writes a single character to the file.int fprintf(FILE *fp, const char *format, ...);
: Writes formatted data to a file.int fscanf(FILE *fp, const char *format, ...);
: Reads formatted data from a file.int getw(FILE *fp);
: Reads an integer from a file.int putw(int w, FILE *fp);
: Writes an integer to a file.char *fgets(char *str, int n, FILE *fp);
: Reads a line of text from a file into a string.str
: The buffer to store the string.n
: The maximum number of characters to read.
int fputs(const char *str, FILE *fp);
: Writes a string to a file.size_t fread(void *ptr, size_t size, size_t nmemb, FILE *fp);
: Reads a block of data from a file.size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *fp);
: Writes a block of data to a file.int remove(const char *filename);
: Deletes a file.int rename(const char *old_filename, const char *new_filename);
: Renames a file.
Random Access Functions
These functions allow you to move the file pointer to any location in the file for random access.
int fseek(FILE *fp, long int offset, int origin);
: Sets the file pointer to a specific position.offset
: Number of bytes to move.origin
: Starting position (SEEK_SET
for beginning,SEEK_CUR
for current,SEEK_END
for end).
void rewind(FILE *fp);
: Resets the file pointer to the beginning of the file.long int ftell(FILE *fp);
: Returns the current position of the file pointer.
File Opening Modes
- Read (
r
,r+
): Opens a file for reading. The file must exist.r+
also allows writing. - Write (
w
,w+
): Opens a file for writing. If the file exists, its content is erased. If not, a new file is created.w+
also allows reading. - Append (
a
,a+
): Opens a file for writing, but at the end of the file. If the file does not exist, a new one is created.a+
also allows reading.
Steps to Work with File in C
- Define File Pointer: Create a pointer of type
FILE
. Example:FILE *fp;
- Open File with Required Mode: Use
fopen()
to open the file. - Read, Write, Append Operations: Perform the desired operation using appropriate functions.
- Close the File: Use
fclose()
to close the file and save changes.
Reading Data from Files:
#include <stdio.h>
int main() {
FILE *fp;
char buffer[100];
fp = fopen("example.txt", "r");
if (fp == NULL) {
printf("Error opening file!\n");
return 1;
}
while (fgets(buffer, 100, fp) != NULL) {
printf("%s", buffer);
}
fclose(fp);
return 0;
}
Writing Data on Files:
#include <stdio.h>
int main() {
FILE *fp;
fp = fopen("example_write.txt", "w");
if (fp == NULL) {
printf("Error creating file!\n");
return 1;
}
fprintf(fp, "Hello, world!\n");
fprintf(fp, "This is a test.");
fclose(fp);
printf("Data written to file successfully.\n");
return 0;
}
Appending Data Files:
#include <stdio.h>
int main() {
FILE *fp;
fp = fopen("example_write.txt", "a");
if (fp == NULL) {
printf("Error opening file!\n");
return 1;
}
fprintf(fp, "\nAppending a new line.");
fclose(fp);
printf("Data appended to file successfully.\n");
return 0;
}
End of File (EOF)
EOF
is a macro defined in stdio.h
that stands for "End of File." It's a special constant value returned by file input functions (like getc()
or fscanf()
) to indicate that the end of the file has been reached, preventing the program from reading beyond the file's content.
5. The typedef
Keyword
The typedef
keyword is used to create an alias or a new name for an existing data type. It is commonly used to make code more readable and portable.
Syntax:
typedef existing_dataType new_name;
Example:
#include <stdio.h>
typedef unsigned int UINT;
typedef struct Student Student;
struct Student {
char name[50];
int rollNumber;
};
int main() {
UINT number = 100;
Student s1; // Using the new alias
s1.rollNumber = 20;
printf("Number: %u\n", number);
printf("Student roll number: %d\n", s1.rollNumber);
return 0;
}