Module-III
CSET333: Problem Solving Using C 1
File Handling in C
File handling in C is the process in which we create, open, read, write,
and close operations on a file. C language provides different functions
such as fopen(), fwrite(), fread(), fseek(), fprintf(), etc. to perform input,
output, and many different C file operations in our program.
In order to understand why file handling is important, let us look at a
few features of using files:
Reusability: The data stored in the file can be accessed, updated, and
deleted anywhere and anytime providing high reusability.
Portability: Without losing any data, files can be transferred to another in
the computer system. The risk of flawed coding is minimized with this
feature.
Efficient: A large amount of input may be required for some programs. File
handling allows you to easily access a part of a file using few instructions
which saves a lot of time and reduces the chance of errors.
Storage Capacity: Files allow you to store a large amount of data without
having to worry about storing everything simultaneously in a program.
CSET333: Problem Solving Using C 2
Types of Files in C
A file can be classified into two types based on the way the file stores
the data. They are as follows:
• Text Files
• Binary Files
CSET333: Problem Solving Using C 3
1. Text Files
A text file contains data in the form of ASCII characters and is
generally used to store a stream of characters.
• Each line in a text file ends with a new line character (‘\n’).
• It can be read or written by any text editor.
• They are generally stored with .txt file extension.
• Text files can also be used to store the source code.
2. Binary Files
A binary file contains data in binary form (i.e. 0’s and 1’s) instead of
ASCII characters. They contain data that is stored in a similar manner to
how it is stored in the main memory.
• The binary files can be created only from within a program and
their contents can only be read by a program.
• More secure as they are not easily readable.
• They are generally stored with .bin file extension.
CSET333: Problem Solving Using C 4
C File Operations
C file operations refer to the different possible operations that we can
perform on a file in C such as:
[Link] a new file – fopen() with attributes as “a” or “a+” or
“w” or “w+”
[Link] an existing file – fopen()
[Link] from file – fscanf() or fgets()
[Link] to a file – fprintf() or fputs()
[Link] to a specific location in a file – fseek(), rewind()
[Link] a file – fclose()
File Pointer in C
Syntax of File Pointer
FILE* pointer_name;
File Pointer is used in almost all the file operations in C.
CSET333: Problem Solving Using C 5
Open a File in C
For opening a file in C, the fopen() function is used with the filename
or file path along with the required access modes.
Syntax of fopen()
FILE* fopen(const char *file_name, const char *access_mode);
Parameters
•file_name: name of the file when present in the same directory as the
source file. Otherwise, full path.
•access_mode: Specifies for what operation the file is being opened.
Return Value
•If the file is opened successfully, returns a file pointer to it.
•If the file is not opened, then returns NULL.
CSET333: Problem Solving Using C 6
Opening
Description
Modes
Searches file. If the file is opened successfully fopen( ) loads it into memory and sets up a
r pointer that points to the first character in it. If the file cannot be opened fopen( ) returns
NULL.
rb Open for reading in binary mode. If the file does not exist, fopen( ) returns NULL.
Open for writing in text mode. If the file exists, its contents are overwritten. If the file
w
doesn’t exist, a new file is created. Returns NULL, if unable to open the file.
Open for writing in binary mode. If the file exists, its contents are overwritten. If the file
wb
does not exist, it will be created.
Searches file. If the file is opened successfully fopen( ) loads it into memory and sets up a
a pointer that points to the last character in it. It opens only in the append mode. If the file
doesn’t exist, a new file is created. Returns NULL, if unable to open the file.
Open for append in binary mode. Data is added to the end of the file. If the file does not
ab
exist, it will be created.
Searches file. It is opened successfully fopen( ) loads it into memory and sets up a pointer
r+
that points to the first character in it. Returns NULL, if unable to open the file.
Open for both reading and writing in binary mode. If the file does not exist, fopen( )
rb+
returns NULL.
CSET333: Problem Solving Using C 7
Opening
Description
Modes
Searches file. If the file exists, its contents are overwritten. If the file doesn’t exist a new
w+
file is created. Returns NULL, if unable to open the file.
Open for both reading and writing in binary mode. If the file exists, its contents are
wb+
overwritten. If the file does not exist, it will be created.
Searches file. If the file is opened successfully fopen( ) loads it into memory and sets up a
pointer that points to the last character in it. It opens the file in both reading and append
a+
mode. If the file doesn’t exist, a new file is created. Returns NULL, if unable to open the
file.
Open for both reading and appending in binary mode. If the file does not exist, it will be
ab+
created.
CSET333: Problem Solving Using C 8
Example of Opening a File C
CSET333: Problem Solving Using C 9
Create a File in C
The fopen() function can not only open a file but also can create a file
if it does not exist already. For that, we have to use the modes that
allow the creation of a file if not found such as w, w+, wb, wb+, a, a+,
ab, and ab+. FILE *fptr;
fptr = fopen("[Link]", "w");
Example
CSET333: Problem Solving Using C 10
Reading From a File
The file read operation in C can be performed using functions fscanf() or
fgets(). Both the functions performed the same operations as that of
scanf and gets but with an additional parameter, the file pointer. There
are also other functions we can use to read from a file. Such functions
are listed below:
Function Description
fscanf() Use formatted string and variable arguments list to take input from a file.
fgets() Input the whole line from the file.
fgetc() Reads a single character from the file.
fgetw() Reads a number from a file.
fread() Reads the specified bytes of data from a binary file.
Example:
FILE * fptr;
fptr = fopen(“[Link]”, “r”);
fscanf(fptr, "%s %s %s %d", str1, str2, str3, &year);
char c = fgetc(fptr);
CSET333: Problem Solving Using C 11
Write to a File
The file write operations can be performed by the functions fprintf() and
fputs() with similarities to read operations. C programming also provides
some other functions that can be used to write data to a file such as:
Function Description
Similar to printf(), this function use formatted string and varible arguments list to print output to
fprintf()
the file.
fputs() Prints the whole line in the file and a newline at the end.
fputc() Prints a single character into the file.
fputw() Prints a number to the file.
fwrite() This functions write the specified amount of bytes to the binary file.
Example:
FILE *fptr ;
fptr = fopen(“[Link]”, “w”);
fprintf(fptr, "%s %s %s %d", "We", "are", "in", 2012);
fputc("a", fptr);
CSET333: Problem Solving Using C 12
Closing a File
The fclose() function is used to close the file. After successful file
operations, you must always close a file to remove it from the memory.
Syntax of fclose()
fclose(file_pointer);
where the file_pointer is the pointer to the opened file.
Example:
FILE *fptr ;
fptr= fopen(“[Link]”, “w”);
---------- Some file Operations -------
fclose(fptr);
CSET333: Problem Solving Using C 13
Read and Write in a Binary File
Till now, we have only discussed text file operations. The operations on a
binary file are similar to text file operations with little difference.
Opening a Binary File
To open a file in binary mode, we use the rb, rb+, ab, ab+, wb, and wb+
access mode in the fopen() function. We also use the .bin file extension
in the binary filename.
Example
fptr = fopen("[Link]", "rb");
CSET333: Problem Solving Using C 14
Write to a Binary File
We use fwrite() function to write data to a binary file. The data is written
to the binary file in the from of bits (0’s and 1’s).
Syntax of fwrite()
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE
*file_pointer);
Parameters:
• ptr: pointer to the block of memory to be written.
• size: size of each element to be written (in bytes).
• nmemb: number of elements.
• file_pointer: FILE pointer to the output file stream.
Return Value:
• Number of objects written.
CSET333: Problem Solving Using C 15
Reading from Binary File
The fread() function can be used to read data from a binary file in C. The
data is read from the file in the same form as it is stored i.e. binary form.
Syntax of fread()
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *file_pointer);
Parameters:
• ptr: pointer to the block of memory to read.
• size: the size of each element to read(in bytes).
• nmemb: number of elements.
• file_pointer: FILE pointer to the input file stream.
Return Value:
• Number of objects written.
CSET333: Problem Solving Using C 16
Functions Description
fopen() It is used to create a file or to open a file.
fclose() It is used to close a file.
fgets() It is used to read a file.
fprintf() It is used to write blocks of data into a file.
fscanf() It is used to read blocks of data from a file.
getc() It is used to read a single character to a file.
putc() It is used to write a single character to a file.
fseek() It is used to set the position of a file pointer to a mentioned location.
ftell() It is used to return the current position of a file pointer.
rewind() It is used to set the file pointer to the beginning of a file.
putw() It is used to write an integer to a file.
getw() It is used to read an integer from a file.
CSET333: Problem Solving Using C 17
Recursive Functions in C
Basic Structure of Recursive Functions
The basic syntax structure of the recursive functions is:
type function_name (args) {
// function statements
// base condition
// recursion case (recursive call)
}
Fundamentals of C Recursion
1. Recursion Case
2. Base Condition
CSET333: Problem Solving Using C 18
How Recursion works in C?
In the nSum() function, Recursive Case is
int res = n + nSum(n - 1);
CSET333: Problem Solving Using C 19
Types of C Recursion
In C, recursion can be classified into different types based on what kind
of recursive case is present. These types are:
[Link] Recursion
1. Head Recursion
2. Tail Recursion
3. Tree Recursion
[Link] Recursion
1. Direct Recursion
Direct recursion is the most common type of recursion, where a function
calls itself directly within its own body. The recursive call can occur once
or multiple times within the function due to which we can further classify
the direct recursion
A. Head Recursion
The head recursion is a linear recursion where the position of its only
recursive call is at the start of the function. It is generally the first
statement in the function.
CSET333: Problem Solving Using C 20
B. Tail Recursion
The tail recursion is also a liner recursion like head recursion but the
position of the recursive call is at the end of the function. Due to this,
the tail recursion can be optimized to minimize the stack memory
usage. This process is called Tail Call Optimization.
In the first example, the nSum() does the tail recursion.
C. Tree Recursion
In tree recursion, there are multiple recursive calls present in the body
of the function. Due to this, while tracing the program flow, it makes a
tree-like structure, hence the name Tree Recursion.
2. Indirect Recursion
Indirect recursion is an interesting form of recursion where a function
calls another function, which eventually calls the first function or any
other function in the chain, leading to a cycle of function calls. In other
words, the functions are mutually recursive. This type of recursion
involves multiple functions collaborating to solve a problem.
CSET333: Problem Solving Using C 21
Advantages of C Recursion
The advantages of using recursive methods over other methods are:
Recursion can effectively reduce the length of the code.
Some problems are easily solved by using recursion like the
tower of Hanoi and tree traversals.
Data structures like linked lists, trees, etc. are recursive by
nature so recursive methods are easier to implement for these
data structures.
Disadvantages of C Recursion
As with almost anything in the world, recursion also comes with certain
limitations some of which are:
Recursive functions make our program a bit slower due to
function call overhead.
Recursion functions always take extra space in the function call
stack due to separate stack frames.
Recursion methods are difficult to understand and implement.
CSET333: Problem Solving Using C 22
Memory Allocation for C Recursive
Function
A stack frame is created on top of the existing stack frames each
time a recursive call is encountered and the data of each recursive
copy of the function will be stored in their respective stack.
Once, some value is returned by the function, its stack frame will be
destroyed.
The compiler maintains an instruction pointer to store the address of
the point where the control should return in the function after its
progressive copy returns some value. This return point is the
statement just after the recursive call.
After all the recursive copy returned some value, we come back to
the base function and the finally return the control to the caller
function.
CSET333: Problem Solving Using C 23
Let’s use the first example again and see how the memory is managed for the
nSum() function.
Step 1:
When nSum() is called from the main() function with 5 as an argument, a stack
frame for nSum(5) is created.
Step 2:
While executing nSum(5), a recursive call is encountered as nSum(4). The
compiler will now create a new stack frame on top of the nSum(5)’s stack frame
and maintain an instruction pointer at the statement where nSum(4) was
encountered.
Step 3:
In the execution of nSum(4), we encounter another recursive call as nSum(3).
The compiler will again follow the same steps and maintain another instruction
pointer and stack frame for nSum(3).
CSET333: Problem Solving Using C 24
Step 4:
The same thing will happen with nSum(3), nSum(2), and nSum(1)’s execution.
Step 5:
But when the control comes to nSum(0), the condition (n == 0) becomes true and
the statement return 0 is executed.
Step 6:
As the value is returned by the nSum(0), the stack frame for the nSum(0) will be
destroyed. Using the instruction pointer, the program control will return to the
nSum(1) function and the nSum(0) call will be replaced by value 0.
CSET333: Problem Solving Using C 25
Step 7:
Now, in nSum(1), the expression int res = 1 + 0 will be evaluated and the
statement return res will be executed. The program control will move to the
nSum(2)
Step 8:
In nSum(2), nSum(1) call will be replaced by the value it returned, which is 1. So,
after evaluating int res = 2 + 1, 3 will be returned to nSum(3). The same thing
will keep happening till the control comes to the nSum(5) again.
Step 9:
When the control reaches the nSum(5), the expression int res = 5 +
nSum(4) will look like int res = 5 + 10. Finally, this value will be returned to the
main() function and the execution of nSum() function will be completed.
CSET333: Problem Solving Using C 26
Dynamic Memory Allocation in C
There are 4 library functions provided by C defined defined
under <stdlib.h> header file to facilitate dynamic memory allocation in C
programming.
They are:
1) malloc()
2) calloc()
3) free()
4) realloc()
CSET333: Problem Solving Using C 27
C malloc() method
Syntax of malloc() in C
ptr = (cast-type*) malloc(byte-size)
For Example:
ptr = (int*) malloc(100 * sizeof(int));
Since the size of int is 4 bytes, this statement will allocate 400 bytes of
memory. And, the pointer ptr holds the address of the first byte in the
allocated memory.
CSET333: Problem Solving Using C 28
C calloc() method
1. “calloc” or “contiguous allocation” method in C is used to
dynamically allocate the specified number of blocks of memory of the
specified type. it is very much similar to malloc() but has two different
points and these are:
2. It initializes each block with a default value ‘0’.
3. It has two parameters or arguments as compare to malloc().
Syntax of calloc() in C
ptr = (cast-type*)calloc(n, element-size);
here, n is the no. of elements and element-size is the size of each
element.
CSET333: Problem Solving Using C 29
C free() method
“free” method in C is used to dynamically de-allocate the memory. The
memory allocated using functions malloc() and calloc() is not de-allocated
on their own. Hence the free() method is used, whenever the dynamic
memory allocation takes place. It helps to reduce wastage of memory by
freeing it.
Syntax of free() in C
free(ptr);
CSET333: Problem Solving Using C 30
C realloc() method
“realloc” or “re-allocation” method in C is used to dynamically change
the memory allocation of a previously allocated memory. In other words, if
the memory previously allocated with the help of malloc or calloc is
insufficient, realloc can be used to dynamically re-allocate memory. re-
allocation of memory maintains the already present value and new blocks
will be initialized with the default garbage value.
Syntax of realloc() in C
ptr = realloc(ptr, newSize);
where ptr is reallocated with new size 'newSize'.
CSET333: Problem Solving Using C 31
Introduction to Memory in C
Two primary types of memory:
1) Global Memory
2) Local Memory
What is Local Memory?
Memory allocated within a function or block.
Scope: Limited to the function where it’s declared.
Lifetime: Only exists during the execution of the function.
Example: void myFunction()
{int localVar = 10; // local memory}
What is Global Memory?
Memory allocated outside all functions, often at the file level.
Scope: Available across the entire program.
Lifetime: Exists throughout the program's execution.
Example: int globalVar = 20; // global memory
void myFunction() {
// can access globalVar here}
CSET333: Problem Solving Using C 32
Characteristics of Local Memory Characteristics of Global Memory
Stored on the stack. Stored in the data segment (or BSS if
uninitialized).
Automatically deallocated when the Accessible from any function within the
function exits. program.
Faster access due to proximity to CPU. Remains in memory throughout program
execution.
Each function call has its own copy of the Can lead to side effects when modified
local variable. globally.
Not accessible outside the defining Increases the risk of naming conflicts.
function.
Pros of Local Memory Pros of Global Memory
Encapsulation: Variables are hidden Useful for data that needs to be shared
within the function. across multiple functions.
Memory efficiency: Freed after the Persists throughout the entire program.
function is executed.
Reduces the risk of accidental Convenient when dealing with
modifications from other parts of the configuration or constant data.
program.
CSET333: Problem Solving Using C 33
Cons of Local Memory Cons of Global Memory
Can’t maintain state across function calls. Hard to manage: Global variables can be
accidentally modified.
Not suitable for data that needs to persist Can lead to naming collisions and
after function completion. debugging difficulties.
Stack overflow risk if too much local Memory waste: Remains allocated even
memory is allocated. when not needed.
Stack vs Data Segment
Local Memory:
• Stored in the stack.
• LIFO (Last In, First Out) memory management.
Global Memory:
• Stored in the data segment.
• Persistent allocation.
CSET333: Problem Solving Using C 34
Error Handling in C
A lot of C function calls return -1 or NULL or set an in case of an error
code as the global variable errno, so quick tests on these values are
easily done with an instance of ‘if statement’.
What is errno?
errno is a global variable indicating the error occurred during any
function call and it is defined inside <errno.h> header file.
When a function is called in C, a variable named errno is automatically
assigned a code (value) which can be used to identify the type of error
that has been encountered. Different codes (values) for errno mean
different types of errors.
CSET333: Problem Solving Using C 35
errno value Error errno value Error
Operation not
1 8 Exec format error
permitted
No such file or
2 9 Bad file number
directory
3 No such process 10 No child processes
Interrupted
4 11 Try again
system call
5 I/O error 12 Out of memory
No such device or
6 13 Permission denied
address
The argument list
7
is too long
CSET333: Problem Solving Using C 36
Different Methods for Error Handling in C
Different methods are used to handle different kinds of errors in C.
Some of the commonly used methods are:
1. perror()
2. strerr()
3. ferror()
4. feof()
5. clearerr()
6. Exit Status
7. Divide by Zero Error
1. perror()
The perror() function is used to show the error description. It displays
the string you pass to it, followed by a colon, a space, and then the
textual representation of the current errno value.
Syntax
void perror(const char *str);
Parameters
• str: It is a string containing a custom message to be printed before
the error message itself.
CSET333: Problem Solving Using C 37
2. strerror()
The strerror() function is also used to show the error description. This
function returns a pointer to the textual representation of the current
errno value.
Syntax
char *strerror(int errnum);
Parameters
• errnum: It is the error number (errno).
3. ferror()
The ferror() function is used to check whether an error occurred during
a file operation.
Syntax
int ferror(FILE *stream);
Parameters
• stream: It is the pointer that points to the FILE for which we want to
check the error.
Return Value
• It returns a non-zero value if an error occurred, otherwise it returns 0.
CSET333: Problem Solving Using C 38
4. feof()
The feof() function is used to check whether end-of-file indicator is set
for a file steam.
Syntax
int feof(FILE *stream);
Parameters
• stream: It is the pointer that points to the FILE for which we want to
check the error.
Return Value
• It returns a non-zero value if an error occurred, otherwise, it returns 0.
5. clearerr()
The clearerr() function is used to clear both end-of-file and error
indicators for a file stream.
Syntax
void clearerr(FILE *stream);
Parameters
• stream: It is the pointer that points to the FILE for which we want to
check the error.
CSET333: Problem Solving Using C 39
6. Exit Status
Exit status is the value returned by the program after its execution is
completed which tells the status of the execution of the program.
The C standard specifies two
constants: EXIT_SUCCESS and EXIT_FAILURE, that may be passed to
exit() to indicate successful or unsuccessful termination, respectively.
These are macros defined in <stdlib.h> header file.
7. Divide by Zero Errors
A common pitfall made by C programmers is not checking if a divisor is
zero before a division command. Division by zero leads to undefined
behavior, there is no C language construct that can do anything about it.
Your best bet is to not divide by zero in the first place, by checking the
denominator.
CSET333: Problem Solving Using C 40
Calling C functions from Python
Why Call C from Python?
Performance: Python is slower than C in performance-critical tasks.
Use of Legacy Code: Reuse existing C code.
Low-level System Access: Use system-level libraries directly from
Python.
Methods to Call C from Python
ctypes: Standard Python library to call C functions directly.
CFFI: C Foreign Function Interface, providing a more Pythonic
approach.
SWIG: Simplifies wrapping C/C++ code.
Python C API: Directly embed C code in Python, most powerful but
complex.
CSET333: Problem Solving Using C 41
Using ctypes
• Overview: Allows calling functions from dynamic/shared libraries
(DLLs/SOs).
• Key Steps:
• Load the shared library using [Link] or [Link].
• Define C function signatures in Python using ctypes types.
• Call the function just like a Python function.
Example:
import ctypes
# Load shared C library
lib = [Link]('./[Link]')
# Define argument and return types for the C function
lib.add_integers.argtypes = (ctypes.c_int, ctypes.c_int)
lib.add_integers.restype = ctypes.c_int
# Call the function
result = lib.add_integers(3, 5)
print(result) # Output: 8
CSET333: Problem Solving Using C 42
Using CFFI
• Overview: A more user-friendly alternative to ctypes.
• Key Steps:
• Define C functions in Python using [Link]().
• Load the C library using [Link]().
• Call the function like a regular Python method.
Example:
from cffi import FFI
ffi = FFI()
[Link]("int add_integers(int, int);")
lib = [Link]('./[Link]')
result = lib.add_integers(3, 5)
print(result) # Output: 8
Using SWIG
• Overview: SWIG generates wrapper code for calling C/C++ from various
languages.
• Steps:
• Write C code and header file.
• Create an interface file (.i file).
• Use SWIG to generate Python wrapper.
• Compile the wrapper and link with the C code.
CSET333: Problem Solving Using C 43
Python C API
• Overview: Provides direct control over Python objects in C.
• When to Use: For tight integration between Python and C with
optimal performance.
• Disadvantages: More complex, requires writing a Python extension
module.
Example:
• Write a C function
• Build as a Python extension using setuptools
• Import and use the C function as a Python module
// example.c
#include <Python.h>
static PyObject* add_integers(PyObject* self, PyObject* args) {
int a, b;
if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
return NULL;
}
return PyLong_FromLong(a + b);
}
CSET333: Problem Solving Using C 44
Calling python functions from C
Why Call Python from C?
Scripting Capabilities: Embed Python to add scripting functionality to
C programs.
Leverage Python Libraries: Access Python’s extensive ecosystem
(e.g., NumPy, Pandas).
Rapid Prototyping: Execute Python logic without rewriting C code.
Python C API Overview
The Python/C API allows C programs to call Python functions.
Embedding Python: This is embedding the Python interpreter into a C
program.
Interpreter Lifecycle:
Initialize the Python interpreter.
Execute Python code.
Finalize the interpreter.
CSET333: Problem Solving Using C 45
Setup: Include Python Headers
• Include the necessary Python headers:
#include <Python.h>
Initializing the Python Interpreter
• Key Function: Py_Initialize()
#include <Python.h>
int main() {
Py_Initialize(); // Initialize Python Interpreter
// Your code to call Python functions goes here
Py_Finalize(); // Finalize the interpreter when done
return 0;
}
CSET333: Problem Solving Using C 46
Steps to Call Python from C
• Initialize the Python Interpreter: Py_Initialize()
• Import Python Module: PyImport_ImportModule()
• Get Python Function: PyObject_GetAttrString()
• Call Python Function: PyObject_CallObject()
• Handle Return Values: Use Python C API to process Python results.
• Finalize the Interpreter: Py_Finalize()
Example Code Structure
#include <Python.h>
int main() {
// Initialize the Python Interpreter
Py_Initialize();
// Import the Python module (e.g., [Link])
PyObject* pModule = PyImport_ImportModule("myscript");
CSET333: Problem Solving Using C 47
if (pModule != NULL) {
// Get the function from the module
PyObject* pFunc = PyObject_GetAttrString(pModule, "my_function");
if (pFunc && PyCallable_Check(pFunc)) {
// Call the function with arguments
PyObject* pValue = PyObject_CallObject(pFunc, NULL);
// Process the returned value
if (pValue != NULL) {
printf("Return value: %ld\n", PyLong_AsLong(pValue));
Py_DECREF(pValue);
}
}
Py_DECREF(pFunc);
Py_DECREF(pModule);
}
// Finalize the Python Interpreter
Py_Finalize();
return 0;
}
CSET333: Problem Solving Using C 48
Creating a thread
What is a Thread?
A thread is the smallest unit of execution within a process.
A process can have multiple threads sharing the same memory space.
Multithreading allows concurrent execution of tasks within a process.
Why Use Threads?
Concurrency: Run multiple tasks simultaneously.
Performance: Utilize multiple CPU cores for better performance.
Responsiveness: Keep applications responsive by offloading long-
running tasks.
Parallelism: Split large computational tasks into smaller threads for
parallel processing.
CSET333: Problem Solving Using C 49
Threads operate faster than processes due to following reasons:
1) Thread creation is much faster.
2) Context switching between threads is much faster.
3) Threads can be terminated easily
4) Communication between threads is faster.
Thread vs Process
• Process:
• Independent execution with its own memory space.
• Communication between processes requires inter-process
communication (IPC).
• Thread:
• Shares memory space with other threads in the same process.
• Lightweight compared to processes.
• Easier and faster communication between threads.
CSET333: Problem Solving Using C 50
Example:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> //Header file for sleep(). man 3 sleep for details.
// A normal C function that is executed as a thread
// when its name is specified in pthread_create()
void* myThreadFun(void* vargp)
{
sleep(1);
printf("Printing GeeksQuiz from Thread \n");
return NULL;
}
int main()
{
pthread_t thread_id;
printf("Before Thread\n");
pthread_create(&thread_id, NULL, myThreadFun, NULL);
pthread_join(thread_id, NULL);
printf("After Thread\n");
exit(0);
}
CSET333: Problem Solving Using C 51
Explanation:
In main(), we declare a variable called thread_id, which is of type
pthread_t, which is an integer used to identify the thread in the system.
After declaring thread_id, we call pthread_create() function to create a
thread.
pthread_create() takes 4 arguments.
The first argument is a pointer to thread_id which is set by this
function.
The second argument specifies attributes. If the value is NULL, then
default attributes shall be used.
The third argument is name of function to be executed for the thread to
be created.
The fourth argument is used to pass arguments to the function,
myThreadFun.
The pthread_join() function for threads is the equivalent of wait() for
processes. A call to pthread_join blocks the calling thread until the
thread with identifier equal to the first argument terminates.
CSET333: Problem Solving Using C 52
How to compile the given program/code?
To compile a multithreaded program using gcc, we need to link it with
the pthreads library. Following is the command used to compile the
program.
gfg@ubuntu:~/$ gcc multithread.c -lpthread
gfg@ubuntu:~/$ ./[Link]
Before Thread
Printing GeeksQuiz from Thread
After Thread
gfg@ubuntu:~/$
CSET333: Problem Solving Using C 53
Passing Arguments to Threads
• In C (pthreads): Use the fourth argument of pthread_create to pass
data.
void* thread_function(void* arg) {
int* num = (int*) arg;
printf("Thread: Number passed: %d\n", *num);
return NULL;
}
int main() {
pthread_t thread;
int num = 10;
pthread_create(&thread, NULL, thread_function, &num);
pthread_join(thread, NULL);
}
CSET333: Problem Solving Using C 54
Thread Synchronization Techniques in C
What is Thread Synchronization?
• Thread Synchronization: Ensuring that multiple threads can work
together without causing race conditions or data corruption.
• Critical Section: A part of the code where shared resources are
accessed, requiring protection.
Thread Synchronization Techniques
• Mutex (Mutual Exclusion)
• Semaphore
• Condition Variables
• Spinlocks
CSET333: Problem Solving Using C 55
Using Mutex in C (POSIX Threads)
• A Mutex allows only one thread to access a critical section at a time,
preventing race conditions.
Mutex Functions:
• pthread_mutex_init: Initialize a mutex.
• pthread_mutex_lock: Lock the mutex (enter critical section).
• pthread_mutex_unlock: Unlock the mutex (leave critical section).
• pthread_mutex_destroy: Destroy the mutex when done.
Example:
#include <pthread.h>
#include <stdio.h>
pthread_mutex_t lock;
int counter = 0;
void* thread_function(void* arg) {
for (int i = 0; i < 100000; i++) {
// Lock the critical section
pthread_mutex_lock(&lock);
counter++; // Critical section
// Unlock the critical section
pthread_mutex_unlock(&lock);
}
return NULL;}
CSET333: Problem Solving Using C 56
int main() {
pthread_t thread1, thread2;
// Initialize the mutex
pthread_mutex_init(&lock, NULL);
// Create two threads
pthread_create(&thread1, NULL, thread_function, NULL);
pthread_create(&thread2, NULL, thread_function, NULL);
// Wait for both threads to finish
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
// Destroy the mutex
pthread_mutex_destroy(&lock);
printf("Final Counter Value: %d\n", counter);
return 0;
}
Key Points:
• Without Mutex: Counter value may be incorrect due to race conditions.
• With Mutex: Ensures correct and consistent counter value.
CSET333: Problem Solving Using C 57
Using Condition Variables in C
• Condition Variables are used for signaling between threads.
• A thread can wait for a condition to become true, and another thread can
signal when that condition is met.
Condition Variable Functions:
• pthread_cond_wait: Wait for a condition.
• pthread_cond_signal: Signal a waiting thread.
• pthread_cond_broadcast: Wake all waiting threads.
Example:
#include <pthread.h>
#include <stdio.h>
pthread_mutex_t lock;
pthread_cond_t cond;
int ready = 0;
void* thread_function(void* arg) {
pthread_mutex_lock(&lock);
while (!ready) {
// Wait for the condition to be signaled
pthread_cond_wait(&cond, &lock);}
CSET333: Problem Solving Using C 58
printf("Thread received signal to proceed.\n");
pthread_mutex_unlock(&lock);
return NULL;
}
int main() {
pthread_t thread;
// Initialize the mutex and condition variable
pthread_mutex_init(&lock, NULL);
pthread_cond_init(&cond, NULL);
// Create the thread
pthread_create(&thread, NULL, thread_function, NULL);
// Simulate some work before signaling the thread
sleep(1);
// Lock and signal the condition
pthread_mutex_lock(&lock);
ready = 1;
pthread_cond_signal(&cond); // Signal the thread to proceed
pthread_mutex_unlock(&lock);
CSET333: Problem Solving Using C 59
// Wait for the thread to finish
pthread_join(thread, NULL);
// Destroy the mutex and condition variable
pthread_mutex_destroy(&lock);
pthread_cond_destroy(&cond);
return 0;
}
Key Points:
• pthread_cond_wait: Waits for the condition to become true.
• pthread_cond_signal: Notifies a thread waiting on the condition.
CSET333: Problem Solving Using C 60