IES - UNIT - 3 - Notes
IES - UNIT - 3 - Notes
The „C‟ program code for the super loop is given below void main ()
{
Configurations (); Initializations ();
while (1)
{
Task 1 ();
Task 2 ();
:
:
Task n ();
}
}
Pros:
Doesn‟t require an Operating System for task scheduling and monitoring and free from OS
related overheads
Simple and straight forward design.
Reduced memory footprint
Enhancements:
Combine Super loop based technique with interrupts
Execute the tasks (like keyboard handling) which require Real time attention as Interrupt
Service routines.
The Embedded OS is responsible for scheduling the execution of user tasks and the
allocation of system resources among multiple tasks
It Involves lot of OS related overheads apart from managing and executing user
defined tasks
Microsoft® Windows XP Embedded is an example of GPOS for embedded devices
Point of Sale (PoS) terminals, Gaming Stations, Tablet PCs etc are examples of
embedded devices running on embedded GPOSs
„Windows CE‟, „Windows Mobile‟,„QNX‟, „VxWorks‟, „ThreadX‟, „MicroC/OS-II‟,
„Embedded Linux‟, „Symbian‟ etc are examples of RTOSs employed in Embedded
Product development
Mobile Phones, PDAs, Flight Control Systems etc are examples of embedded devices
that runs on RTOSs
;###############################################################
; SUBROUTINE FOR GENERATING DELAY
; DELAY PARAMETR PASSED THROUGH REGISTER R1
; RETURN VALUE NONE, REGISTERS USED: R0, R1
;###############################################################
Advantages:
Efficient Code Memory & Data Memory Usage (Memory Optimization):
o The developer is well aware of the target processor architecture and memory
organization, so optimized code can be written for performing operations.
o This leads to less utilization of code memory and efficient utilization of data
memory.
High Performance:
o Optimized code not only improves the code memory usage but also improves the
total system performance.
o Through effective assembly coding, optimum performance can be achieved for
target processor.
Low level Hardware Access:
o Most of the code for low level programming like accessing external device specific
registers from OS kernel ,device drivers, and low level interrupt routines, etc are
making use of direct assembly coding.
Drawbacks:
High Development time:
o The developer takes lot of time to study about architecture, memory organization,
addressing modes and instruction set of target processor/controller.
o More lines of assembly code is required for performing a simple action.
Developer dependency:
o There is no common written rule for developing assembly language based
applications.
Non portable:
o Target applications written in assembly instructions are valid only for that
particular family of processors and cannot be re-used for other target
processors/controllers.
o If the target processor/controller changes, a complete re-writing of the application
using assembly language for new target processor/controller is required.
Advantages:
Reduced Development time: Developer requires less or little knowledge on
internal hardware details and architecture of the target processor/Controller.
Developer independency: The syntax used by most of the high level languages
are universal and a program written high level can easily understand by a second
person knowing the syntax of the language
Portability: An Application written in high level language for particular target
processor /controller can be easily be converted to another target
processor/controller specific application with little or less effort
i. Mixing Assembly Language with High level language like ‘C’ (Assembly Language
with ‘C’):
Assembly routines are mixed with „C‟ in situations where the entire program is written
in „C‟ and the cross compiler in use do not have built in support for implementing
certain features like ISR.
If the programmer wants to take advantage of the speed and optimized code offered by
the machine code generated by hand written assembly rather than cross compiler
generated machine code.
For accessing certain low level hardware, the timing specifications may be very
critical and cross compiler generated machine code may not be able to offer the
required time specifications accurately.
Writing the hardware/peripheral access routine in processor/controller specific
assembly language and invoking it from „C‟ is the most advised method.
Mixing „C‟ and assembly is little complicated.
The programmer must be aware of how to pass parameters from the „C‟ routine to
assembly and values returned from assembly routine to „C‟ and how Assembly routine
is invoked from the „C‟ code.
Passing parameter to the assembly routine and returning values from the assembly
routine to the caller „C‟ function and the method of invoking the assembly routine
from „C‟ code is cross compiler dependent.
There is no universal written rule for purpose.
We can get this information from documentation of the cross compiler.
Different cross compilers implement these features in different ways depending on
GPRs and memory supported by target processor/controller
Embedded „C‟ can be considered as a subset of conventional „C‟ language. Embedded „C‟
supports all „C‟ instructions and incorporates a few target processor specific
functions/instructions. It should be noted that the standard ANSI „C‟ library implementation is
always tailored to the target processor/controller library files in Embedded „C‟. The
implementation of target processor/controller specific functions/instructions depends upon the
processor/controller as well as the supported cross-compiler for the particular Embedded „C‟
language. A software program called „Cross-compiler‟ is used for the conversion of programs
written in Embedded „C‟ to target processor/controller specific instructions (machine language).
Keywords and Identifiers Keywords are the reserved names used by the „C‟ language. All
keywords have a fixed meaning in the „C‟ language context and they are not allowed for
programmers for naming their own variables or functions. ANSI „C* supports 32 keywords and
they are listed below.
All „C‟ supported keywords should be written in „lowercase‟ letters.
Identifiers are user defined names and labels. Identifiers can contain letters of English alphabet
(both upper and lower case) and numbers. The starting character of an identifier should be a letter.
The only special character allowed in identifier is underscore (_).
The data type size and range given above is for an ordinary „C‟ compiler for 32 bit platform. It
should be noted that the storage size may vary for data type depending on the cross-compiler in
use for embedded applications.
Since memory is a big constraint in embedded applications, select the optimum data type for a
variable. For example if the variable is expected to be within the range 0 to 255, declare the same
as an „unsigned char' or „unsigned short int data type instead of declaring it as „int‟ or „unsigned
int‟. This will definitely save considerable amount of memory.
Storage Class Keywords related to storage class provide information on the scope (visibility or
accessibility) and life time (existence) of a variable. „C‟ supports four types of storage classes and
they are listed below.
Apart from these four storage classes, „C‟ literally supports storage class 'global'. An „auto or
static‟ variable declared in the public space of a file (declared before the implementation of all
functions including main in a file) is accessible to all functions within that file. There is no
explicit storage class for „global'. The way of declaration of a variable determines whether it is
global or not.
Logical Operations Logical operations are usually performed for decision making and
program control transfer. The list of logical operations supported by „C‟ are listed below
Relational Operations Relational operations are normally performed for decision making and
program control transfer on the basis of comparison. Relational operations supported by „C‟ are listed
below.
Let‟s consider a typical example for a looping instruction in embedded C application. I have a
device which is memory mapped to the processor and I‟m supposed to read the various registers
of it (except status register) only after the contents of its status register, which is memory mapped
at 0x3000 shows device is ready (say value 0x01 means device is ready). I can achieve this by
different ways as given below.
The instruction char *status_reg = (char*) 0x3000; declares status_reg as a character pointer
pointing to location 0x3000. The character pointer is used since the external device‟s register is
only 8bitwide. We will discuss the pointer based memory mapping technique in a later section. In
order to avoid compiler optimization, the pointer should be declared as volatile pointer.
Pointers Pointer is a flexible at the same time most dangerous feature, capable of creating
potential damages leading to firmware crash, if not used properly. Pointer is a memory pointing
based technique for variable access and modification. Pointers are very helpful in
1. Accessing and modifying variables
2. Increasing speed of execution
3. Accessing contents within a block of memory
4. Passing variables to functions by eliminating the use of a local copy of variables
5. Dynamic memory allocation
To understand the pointer concept, let us have a look at the data memory organization of a
processor. For a processor/controller with 128 bytes user programmable internal RAM (e.g.
AT89C51), the memory is organized as:
Characters and Strings Character is a one byte data type and it can hold values ranging
from 0 to 255 (unsigned character) or -128 to +127 (signed character). The term character literally
refers to the alpha numeric characters (English alphabet A to Z (both small letters and capital
letters) and number representation from „0‟ to „9‟) and special characters like *,?,!, etc. An integer
value ranging from 0 to 255 stored in a memory location can be viewed in different ways. For
example, the hexadecimal number 0x30 when represented as a character will give the character
„0‟ and if it is viewed as a decimal number it will give 48. String is an array of characters. A
group of characters defined within a double quote represents a constant string.
„H‟ is an example for a character, whereas “Hello" is an example for a string. String always
terminates with a „\0‟ character. The „\0‟ character indicates the string termination. Whenever you
declare a string using a character array, allocate space for the null terminator „\0‟; in the array
length.
String operations are very important in embedded product applications. Many of the embedded
products contain visual indicators in the form of alpha numeric displays and are used for
displaying text. Though the conventional alpha numeric displays are giving way to graphic
displays, they are deployed widely in low cost embedded products.
Library functions are the built in functions which is either part of the standard „Embedded C‟
library or user created library files. „C‟ provides extensive built in library file support and the
library files are categorized into various types like I/O library functions, string operation library
functions, memory allocation library functions etc. printf(), scanf (), etc. are examples of I/O
library functions. strcpy(), strcmp(), etc. are examples for string operations library functions.
Malloc(), calloc() etc are examples of memory allocation library functions supported by „C‟. All
library functions supported by a particular library is implemented and exported in the same. A
corresponding header („.A‟) file for the library file provides information about the various
functions available to user in a library file. Users should include the header file corresponding to a
particular library file for calling the functions from that library in the „C‟ source file. For example,
if a programmer wants to use the standard I/O library function printf() in the source file, the
header file corresponding to the I/O library file (“ stdio.h ” meant for standard i/o) should be
included in the „c‟ source code using the #include preprocessor directive.
“string.h” is the header file corresponding to the built in library functions for string operations
and “malloc.h” is the header file for memory allocation library' functions. Readers are requested
to get info on header files for other required libraries from the standard ANSI library. As
mentioned earlier, the standard „C‟ library function implementation may be tailored-by cross-
compilers, keeping the function name and parameter list unchanged depending on Embedded „C‟
application requirements. printf() function implemented by C51 cross compiler for 8051
microcontroller is a typical example. The library functions are standardized functions and they
have a unique style of naming convention and arguments. Users should strictly follow-it while
using library functions.
User defined functions are programmer created functions for various reasons like modularity,
easy understanding of code, code reusability, etc. The generic syntax for a function definition
(implementation) is illustrated below.
The task to be executed by the function is implemented within the body of the function. In certain
situations, you may have a single source („.c‟) file containing the entire variable list, user defined
functions, function main(), etc. There are two methods for writing user defined functions if the
source code is a single „.c‟ file. The first method is writing all user defined functions on top of the
main function and other user defined functions calling the user defined function.
If you are writing the user defined functions after the entry' function main() and calling the same
inside main, you should specify the function prototype (function declaration) of each user defined
The „Linkage Type‟ specifies the linkage for the function. It can be either „external‟ or „internal‟.
The „static‟ keyword for the „ Linkage Type‟ specifies the linkage of the function as internal
whereas the „extern‟ „Linkage Type' specifies „external‟ linkage for the function. It is not
mandatory to specify the name of the argument along with its type in the argument list of the
function declaration. The declarations can simply specify the types of parameters in the argument
list. This is called function prototyping. A function prototype consists of the function return type,
the name of the function, and the parameter list. The usage is illustrated below.
Where, „return_type‟ represents the return type of the function, „pointer_name‟ represents the
name of the pointer and „argument list‟ represents the data type of the arguments of the function.
If the function contains multiple arguments, the data types are separated using “,”. Typical
declarations of function pointer are given below.
The parentheses () around the function pointer variable differentiates it as a function pointer
variable. If no parentheses are used, the declaration will look like
struct_name is the name of the structure and the choice of the name is left to the programmer.
Let us examine the details kept in an employee record for an employee in the employee database
of an organization as example. A typical employee record contains information like „Employee
Name', „Employee Code‟, and „Date of Birth‟. This information can be represented in „C‟ using a
structure as given below.
Unions Union is a concept derived from structure and union declarations follow the same syntax
as that of structures (structure tag is replaced by union tag). Though union looks similar to
structure in declaration, it differs from structure in the memory allocation technique for the
member variables. Whenever a union variable is created, memory is allocated only to the member
variable of union requiring the maximum storage size. But for structures, memory is allocated to
each member variables. This helps in efficient data memory usage. Even if a union variable
contains different member variables of different data types, existence is only for a single variable
at a time. The size of a union returns the storage size of its member variable occupying the
maximum storage size. The syntax for declaring union is given below:
1. The offsetof() macro returns the offset of a member variable (in bytes) from the beginning
of its parent structure. The usage is offsetof (structName, memberName); where
„structName‟ is the name of the parent structure and „ memberName ‟ is the name of the
member in the parent data structure whose offset is to be determined. For using this macro
use the header file „ stddef.h ‟
2. If you declare a structure just before the main () function in your source file, ensure that
the structure is terminated with the structure definition termination indicator Otherwise
function main() will be treated as a structure and the application may crash with
exceptions.
3. A union variable can be initialized at the time of creation with the first member variable
value only.
Macro substitution pre-processor directives Macros are a means of creating portable inline code.
„Inline‟ means wherever the macro is called in a source file it is replaced directly with the code
defined by the macro. In-line code improves the performance in terms of execution speed. Macros
are similar to subroutines in functioning but they differ in the way in which they are coded in the
source code. Functions are normally written only once with arguments. Whenever a function
needs to be executed, a call to the function code is made with the required parameters at the point
where it needs to be invoked. If a macro is used instead of functions, the compiler inserts the code
for macro wherever it is called. From a code size viewpoint macros are non-optimized compared
to functions. Macros are generally used for coding small functions requiring execution without
latency. The „#define‟ pre-processor directive is used for coding macros. Macros can be either
simple definitions or functions with arguments.
Constant data Constant data informs that the data held by a variable is always constant and
cannot be modified by the application. Constants used as scaling variables, ratio factors, various
scientific computing constants (e.g. Plank‟s constant), etc. are represented as constant data. The
general form of declaring a constant variable given below.
„const‟ is the keyword informing compiler/cross compiler that the variable is constant, „data type‟
gives the data type of the variable. It can be „int‟, „char‟, „float‟, etc. „variable name‟ is the
constant variable.
const float PI = 3.1417;
float const PI = 3.1417;
The ‘Volatile’ Type Qualifier in Embedded „C‟ The keyword „ volatile ‟ prefixed with any
variable as qualifier informs the cross-compiler that the value of the variable is subject to change
at any point of time (subject to asynchronous modification) other than the current statement of
code where the control is at present.
1. Variables common to Interrupt Service Routines (ISR) and other functions of a file.
2. Memory mapped hardware registers.
3. Variables shared by different threads in a multi threaded application (Not applicable to Super
loop Firmware development approach)
The „volatile‟ keyword informs the cross-compiler that the variable with „volatile‟ qualifier is
subject to asynchronous change and there by the cross compiler turns off any optimization
(assumptions on the variable) for these variables. The general form of declaring a volatile variable
is given below.
Infinite loop using while The following code snippet illustrates „while‟ for infinite loop
implementation.
Which technique is the best? According to all experienced Embedded „C‟ programmers while()
loop is the best choice for creating infinite loops. There is no technical reason for this. The clean
syntax of while loop entitles it for the same. The syntax of for loop for infinite loop is little
puzzling and it is not capable of conveying its intended use. „goto‟ is the favorite choice of
programmers migrating from Assembly to Embedded C ©.
break; statement is used for coming out of an infinite loop. You may think why we implement an
infinite loop and then quitting it? Answer - There may be instructions checking some condition
inside the infinite loop. If the condition is met the program control may have to transfer to some
other location.
Bit Manipulation Operations Though Embedded „C‟ does not support a built in Boolean
variable (Bit variable) for holding a „TRUE (Logic 1)‟ or „FALSE (Logic 0)‟ condition, it
provides extensive support for Bit manipulation operations. Boolean variables required in
embedded application are quite often stored as variables with least storage memory requirement
(obviously char variable). Indeed it is wastage of memory if the application contains large number
of Boolean variables and each variable is stored as a char variable. Only one bit (Least Significant
bit) in a char variable is used for storing Boolean information. Rest 7 bits are left unused. This
will definitely lead to serious memory bottle neck. Considerable amount of memory can be saved
if different Boolean variables in an application are packed into a single variable in „C‟ which
requires less memory storage bytes. A character variable can accommodate 8 Boolean variables.
If the Boolean variables are packed for saving memory, depending upon the program requirement
each variable may have to be extracted and some manipulation (setting, clearing, inverting, etc.)
needs to be performed on the bits. The following Bit manipulation operations are employed for
the same.
Bitwise Exclusive OR- XOR Bitwise XOR operator „^‟ acts on individual operand bits and
performs an „Excusive OR‟ operation on the bits. Bitwise XOR operation is used for toggling bits
in embedded applications.
Bitwise NOT Bitwise NOT operations negates (inverts) the state of a bit. The operator (tilde) is
used as the Bitwise NOT operator in C.
Setting and Clearing and Bits Setting the value of a bit to „1‟ is achieved by a Bitwise OR
operation. For example consider a character variable (8bit variable) flag. The following
instruction sets its 0th bit always 1.
Toggling bits
Toggling a bit is performed to negate (toggle) the current state of a bit. If current state of a
specified bit is „1 ‟, after toggling it becomes „0‟ and vice versa. Toggling is also known as
inverting bits. The Bitwise XOR operator is used for toggling the state of a desired bit in an
operand.
Testing bits
So far we discussed the Bitwise operators for changing the status of a selected bit. Bitwise
operators can also be used for checking the present status of a bit without modifying it for
decision making operations.
This instruction examines the status of the 6 th bit of variable flag. The same can also be achieved
by using a constant bit mask as
Coding Interrupt Service Routines (ISR) Interrupt is an event that stops the current
execution of a process (task) in the CPU and transfers the program execution to an address in
code memory where the service routine for the event is located. The event which stops the current
execution can be an internal event or an external event or a proper combination of the external
and internal event. Any trigger signal coming from an externally interfaced device demanding
immediate attentions is an example for external event whereas the trigger indicating the overflow
of an internal timer is an example for internal event. Reception of serial data through the serial
line is a combination of internal and external events. The number of interrupts supported, their
priority levels, interrupt trigger type and the structure of interrupts are processor/controller
architecture dependent and it varies from processor to processor. Interrupts are generally
classified into two: Maskable Interrupts and Non-maskable Interrupts (NMI). Maskable interrupt
can be ignored by the CPU if the interrupt is internally not enabled or if the CPU is currently
engaged in processing another interrupt which is at high priority. Non-maskable interrupts are
interrupts which require urgent attention and cannot be ignored by the CPU. Reset (RST) interrupt
and TRAP interrupt of 8085 processor are examples for Non-maskable interrupts.
Normal functions will not incorporate code for saving the current context before executing the
function and retrieve the saved context before exiting the function. ISR should incorporate code
for performing operations.
Current context saving instructions are written on top of the call to the „C‟ function and context
retrieving instructions are written just below the „C‟ function call. It is little puzzling to find
answers to the following questions in this approach.
1. Which registers must be saved and restored since we are not sure which registers are used by
the cross compiler for implementing the „C‟ function?
2. How the assembly instructions can be interfaced with high-level language like „C‟?
Answers to these questions are cross compiler dependent and you need to find the answer by
referring the documentation files of the cross-compiler in use.
Recursive Functions A function which calls itself repeatedly is called a Recursive Function.
Using recursion, a complex problem is split into its single simplest form. The recursive function
only knows how to solve that simplest case. Recursive-functions are useful in evaluating certain
types of mathematical function, creating and accessing dynamic data structures such as linked
lists or binary trees.
Recursion vs. Iteration: A comparison, both recursion and iteration is used for implementing
certain operations which are self repetitive in some form.
Recursion involves a lot of call stack overhead and function calls. Hence it is slower in
operation compared to the iterative method. Since recursion implements the functionality
with repeated self function calls, more stack memory is required for storing the local
variables and the function return address
Recursion is the best method for implementing certain operations like certain
mathematical operation, creating and accessing of dynamic data structures such as linked
lists or binary trees
A recursive solution implementation can always be replaced by iteration. The process of
converting a recursive function to iterative method is called „unrolling‟
Certain category of embedded applications deal with fixed number of variables with fixed length
and certain other applications deal with variables with fixed memory length as well as variable
with total storage size determined only at the runtime of application (e.g. character array with
variable size). If the number of variables are fixed in an application and if it doesn‟t require a
variable size at run time, the cross compiler can determine the storage memory required by the
application well in advance at the run time and can assign each variable an absolute address or
relative (indirect) address within the data memory. Here the memory required is fixed and
allocation is done before the execution of the application. This type of memory allocation is
referred as „Static Memory Allocation'. The term „Static‟ mentioned here refers 'fixed‟ and it is no
way related to the storage class static. As mentioned, some embedded applications require data
memory which is a combination of fixed memory (Number of variables and variable size is
known prior to cross compilation) and variable length data memory. As an example, let‟s take the
scenario where an application deals with reading a stream of character data from an external
environment and the length of the stream is variable. It can vary between any numbers (say 1 to
100 bytes). The application needs to store the data in data memory temporarily for some
calculation and it can be ignored after the calculation. This scenario can be handled in two ways
in the application program. In the first approach, allocate fixed memory with maximum size (say.
100 bytes) for storing the incoming data bytes from the stream. In the second approach allocate
memory at run .time of the application and de-allocate (free) the memory once the data memory
storage requirement is over. In the first approach if the memory is allocated fixedly, it is locked
forever, and cannot re-used by the application even if there is no requirement for the allocated
number of bytes and it will definitely create memory bottleneck issues in embedded systems
where memory is a big constraint. Hence it is not advised to go for fixed memory allocations for
Dynamic memory allocation technique is employed in Operating System (OS) based embedded
systems. Operating system contains a „Memory Management Unit‟ and it is responsible for
handling memory allocation related operations. The memory management unit allocates memory
to hold the code for the application and the variables associated with the application. The
conceptual view of storage of an application and the variables related to the application is
represented in Figure below
Memory manager allocates a memory segment to each application and the memory segment holds
the source code of the application as well as data related to the application. The actual program
code (executable machine code instructions) is stored at the lower address of the memory (at the
beginning address of the memory segment and stores upward). Constant data related to the
application (e.'g. const int x = 10;) is stored at the memory area just above the executable code.
The alterable data (global and static variables; e.g. static int j = 10; int k= 2; (global declaration),
etc.) are stored at the „Alterable Data ' memory area. The size of the executable code, the number
of constant data and alterable data in an application is always fixed and hence they occupy only a
fixed portion of the memory segment. Memory allocated for the executable code, constant data
and alterable data together constitute the 'Static storage memory (Fixed storage memory)'. The
storage memory available within the memory segment excluding the fixed memory is the
„Dynamic storage memory'. Dynamic storage memory area is divided into two separate entities
namely 'stack' and 'heap'. 'Stack memory' normally starts at the high memory-area (At the top
malloc(): malloc() function allocates a block of memory dynamically. The malloc() function
reserves a block of memory of size specified as parameter to the function, in the heap memory
and returns a pointer of type void. This can be assigned to a pointer of any valid type. The general
form of using malloc() function is given below.
where „pointer' is
a pointer of type „pointer_type‟. „pointer_type‟ can be 'int', 'char', 'float' etc. malloc() function
returns a pointer of type „pointer_type‟ to a block of memory with size 'no. of bytes‟. A typical
example is given below.
This instruction allocates 50 bytes (If there is 50 bytes of memory available in the heap area) and
the address of the first byte of the allocated memory in the heap area is assigned to the pointer ptr
of type char. It should be noted that the malloc() function allocates only the requested number of
bytes and it will not allocate memory automatically for the units of the pointer in use. For
example, if the programmer wants to allocate memory for 100 integers dynamically, the following
code
will not allocate memory size for 100 integers, instead it allocates memory for just 100 bytes. In
order to make it reserving memory for 100 integer variables, the code should be re-written as
Remember malloc() only allocates required bytes of memory and will not initialise the allocated
memory. The allocated memory contains random data.
calloc() The library function calloc() allocates multiple blocks of storage bytes and initialises each
allocated byte to zero. Syntax of calloc() function, is illustrated below.
where „pointer‟ is a pointer of type „pointer_type‟. „pointer_type‟ can be „int‟, „char‟, „float‟ etc.
„n‟ stands for the number of blocks to be allocated and „size of block‟ tells the size of bytes
required per block. The calloc(n, size of block) function allocates continuous memory for „n‟
number of blocks with „size of block‟ number of bytes per block and returns a pointer of type
„pointer_type‟ pointing to the first byte of the allocated block of memory. A typical example is
given below.
Above instruction allocates 50 contiguous blocks of memory each of size one byte in the heap
memory and assign the address of the first byte of the allocated memory region to the character
pointer „ptr'. Since malloc() is capable of allocating only fixed number of bytes in the heap area
regardless of the storage type, calloc() can be used for overcoming this limitation as discussed
below.
This instruction allocates 50 blocks of memory, each block representing an integer variable and
initializes the allocated memory to zero. Similar to the malloc() function, calloc() also returns a
„NULL‟ pointer if there is not enough space in the heap area for allocating storage for the
requested number of memory by the calloc() function. Hence it is advised to check the pointer to
which the calloc() assigns the address of the first byte in the allocated memory area. If calloc()
function fails, the return value will be „NULL‟ and the pointer also will be „NULL‟. Checking the
For example
The function memset (ptr, 0, n * size of. block) sets the memory block of size (n * size of block)
with starting address pointed by „ptr‟ to zero.
free() The „C‟ memory management library function free() is used for releasing or de-allocating
the memory allocated in the heap memory by malloc() or calloc() functions. If memory is
allocated dynamically, the programmer should release it if the dynamically allocated memory is
no longer required for any operation. Releasing the dynamically allocated memory makes it ready
to use for other dynamic allocations. The syntax of free() function is given below.
„ptr‟ is the valid pointer returned by the calloc() or malloc() function on dynamic memory
allocation. Use of an invalid pointer with function free() may result in the unexpected behaviour
of the application.
Note:
1. The dynamic memory allocated using malloc() or calloc() functions should be released
(deallocated)
2. Any use of a pointer which refers to freed memory space results in abnormal behaviour of
application:
3. If the parameter to free() function is not a valid pointer, the application behaviour may be
unexpected.
realloc() realloc() function is used for changing the size of allocated bytes in a dynamically
allocated memory block. You-may come across situations where the allocated memory is not
sufficient to hold the required data or it is surplus in terms of allocated memory bytes. Both of
these situations are handled using realloc() function. The realloc() function changes the size of the
block of memory pointed to, by the pointer parameter to the number of bytes specified by the-
modified size parameter and it returns a new pointer to the block. The pointer specified by the
pointer parameter must have been created with the malloc, calloc, or realloc subroutines and not
been de-allocated with the free or realloc subroutines. Function realloc() may shift the position of
the already allocated block depending on the new size, with preserving the contents of the already