Errors in C Programming
Errors in C Programming
Day 2(Advanced C)
Runtime Errors
Runtime errors are usually the easiest to fix. Some types of runtime errors are:
Segmentation Violation. This error indicates that the program tried to dereference
a pointer containing a bad value.
Stack Overflow. The program tried to use too many temporary variables.
Sometimes, stack overflow happens because the program is too big or is using too
many big temporary arrays, but most of the time this is due to infinite recursion
problems. Almost all UNIX systems automatically check for this error. Turbo C++ and
Borland C++ check for stack overflow only if the
compile -time option -N is used.
#include <stdio.h>
int main()
{
int i,j; /* two random integers */
i = 1;
j = 0;
printf("Starting\n");
printf("Before divide...");
i = i / j; /* divide by zero error */
printf("After\n");
return(0);
}
When run, this program outputs the following: Starting, Floating exception
(core dumped.) This program might lead you to think the divide had never started,
when in fact it had. What happened to the message "Before divide..."? The printf
statement executed and put the message in a buffer, and then the program died. The
buffer never got a chance to be emptied. By putting explicit flush buffer commands
inside the code, we get a truer picture of what is happening. See Example 15-11.
Example
[File: flush2/flush2.c]
#include <stdio.h>
int main()
{
int i,j; /* two random integers */
i = 1;
j = 0;
printf("Starting\n");
fflush(stdout);
printf("Before divide...");
fflush(stdout);
2
The flush statement makes the I/O less efficient, but more current.
1.000E-9 x 1.000E-9
The result is:
1.0 x 10 -18
Because -18 is too small to fit into one digit, we have underflow.
Roundoff Error
Floating-point arithmetic is not exact. Everyone knows that 1+1 is 2, but did you
know that 1/3 + 1/3 does not equal 2/3?
This result can be shown by the following floating-point calculations:
2/3 as floating -point is 6.667E-1.
1/3 as floating -point is 3.333-1.
+3.333E-1
+3.333E-1
+6.666E-1 or 0.6666
which is not:
+6.667E-1
Every computer has a similar problem with its floating point. For example, the
number 0.2 has no exact representation in binary floating-point. Floating-point
arithmetic should never be used for money. Because we are used to dealing with
dollars and cents, you might be tempted to define the amount of $1.98 as:
float amount = 1.98;
However, the more calculations you do with floating-point arithmetic, the bigger the
roundoff error. Banks, credit card companies, and the IRS tend to be very fussy about
money. Giving the IRS a check that's almost right is not going to make them happy.
Money should be stored as an integer number of pennies.
3
Accuracy
How many digits of the fraction are accurate? At first glance you might be tempted to
say all four digits. Those of you who have read the previous section on round off error
might be tempted to change your answer to three.
The answer is: the accuracy depends on the calculation. Certain operations, like
subtracting two numbers that are close to each other, generate inexact results. For
example, consider the following equation:
1 - 1/3 - 1/3 - 1/3
1.000E+0
- 3.333E-1
- 3.333E-1
- 3.333E-1
or:
1.000E+0
- 0.333E+0
- 0.333E+0
- 0.333E+0
_____________________
0.0010E+0 or 1.000E-3
The correct answer is 0.000E+0 and we got 1.000E-3. The very first digit of the
fraction is wrong. This error is an example of the problem called "roundoff error" that
can occur during floating-point operations.
Floating-point by its very nature is not exact. People tend to think of computers as
very accurate machines. They can be, but they also can give wildly wrong results. You
should be aware of the places where errors can slip into your program.
Determining Accuracy
There is a simple way of determining how accurate your floating point is (for simple
calculations). The method used in the following program is to add 1.0+0.1, 1.0+0.01,
1.0+0.001, and so on until the second number gets so small that it makes no
difference in the result. The C language specifies that all floating-point numbers are
to be done in double . This method means that the expression:
float number1, number2;
. . .
while (number1 + number2 != number1)
is equivalent to: while (double(number1) + double(number2) !=
double(number1)) When using the 1+0.001 trick, the automatic conversion of float
to double may give a distorted picture of the accuracy of your machine. (In one case,
84 bits of accuracy were reported for a 32-bit format.) Example 16-1 computes both
the accuracy of floating-point numbers as used in equations and floating-point
4
numbers as stored in memory. Note the trick used to determine the accuracy of the
floating-point numbers in storage.
float/float.c
#include <stdio.h>
int main()
{
/* two numbers to work with */
float number1, number2;
float result; /* result of calculation */
int counter; /* loop counter and accuracy check */
number1 = 1.0;
number2 = 1.0;
counter = 0;
while (number1 + number2 != number1) {
++counter;
number2 = number2 / 10.0;
}
printf("%2d digits accuracy in calculations\n", counter);
number2 = 1.0;
counter = 0;
while (1) {
result = number1 + number2;
if (result == number1)
break;
++counter;
number2 = number2 / 10.0;
}
printf("%2d digits accuracy in storage\n", counter);
return (0);
}
Running this on a Sun-3/50 with a MC68881 floating -point chip, we get: 20 digits
accuracy in calculations
8 digits accuracy in storage This program gives only an approximation of the
floating -point precision arithmetic. A more precise definition can be found in the
standard include file float.h.
5
Day 3(Advanced C)
TSO means terminate but stay outside. It is that program, which release the main memory after the
execution of the program. Example ms paint, notepad, turbo c compilers etc.
TSR means terminate but stay residence .It is those program, which after the execution of the program does
not release the RAM (main memory).e.g. antivirus.
In c any hexadecimal number start with 0x 0r 0X So, address range will be 0x00000 to 0xFFFFF. So in
turbo C 3.0 memory address of all variables must be within 0x00000 to oxFFFFF.
It is 1MB memory range. Note. 2^10 = 1KB / 2^20 = 1MB / 2^30 = 1GB Where 10, 20, 30 are number of
bit.
Resident memory of computer
RAM has divided into two parts: (1) Extended memory (useless) (2) Residence memory
When any program is executed it is stored in the residence memory. For turbo c 3.0, it has 1MB residence
memory i.e. when we open turbo c 3.0 it stores 1MB in the RAM.
All the c variables are stored in the residence memory. In turbo C 3.0, 20 bits address of the memory cell is
known as physical address or real address. In 20 bits, we can represent address from 0x00000 to 0xFFFFF.
That is all c variables must have memory address within this range.
A C programmer cannot not decides what will be the memory address of any variables. It is decided by
compiler. For Example: What will be output of following c code
But we can say in 16 bits compilers address must be within 0x0000 to 0xFFFF and in 32 bits compilers
memory address must be within 0x00000000 to 0xFFFFFFFF.
Note: Suppose your c compiler is based on the microprocessor which total address buses are 32 then its
total size of addressable memory will be:
= 2 ^ 32 bytes
= 4 GB
6
Resident memory of RAM of size 1MB has divided into 16 equal parts. These parts is called segment. Each
segment has size is 64 KB.
16 * 64 KB = 1 MB
This process of division is known as segmentation.
Note: In turbo c 3.0 physical addresses of any variables
are stored in the 20 bits. But we have not any pointers
1. Near pointer
2. Far pointer
3. Huge pointer
So, in the other words we can say memory address of any variable in c has two parts segment number and
offset address. In turbo c 3.0 a particular segment number offset address varies from 0x0000 to 0xFFFF
Suppose physical address of any variable in c is 0x500F1. Then its segment number is 5 and offset address is
00F1.
#include<stdio.h>
int main(){
int x;
printf("%u ",&x); //To print offset address
printf("%p ",x); //To print segment address
printf("%p ",&x); //To print offset address
printf("%fp ",&x); //To print segment address : offset address
return 0;
}
Data segment
All the segments are used for specific purpose. Like segment number 15 is used for ROM, segment number
14 is used for BIOS etc.
7
Segment number eight has special name which is known as data segment.
This segment has been divided into four parts. This is very important for c
programming.
1. Stack area
All automatic variables and constants are stored into stack area. Automatic variables and constants in c:
Variables in the stack area are always deleted when program control reaches it out of scope. Due to this
stack area is also called temporary memory area. For example:
#include<stdio.h>
int main(){
int i;
for(i=0;i<3;i++){
int a=5;
printf("%d",a);
}
return 0;
}
Output: 5 5 5
Explanation: Since variable a is automatic variable, it will store in the stack area. Scope of variable a is
within for loop. So after each iteration variable a will be deleted from stack and in each iteration variable a
will initialize.
#include<stdio.h>
int main(){
int a =5, b = 6, c = 7,d =8;
printf("%d %d %d");
return 0;
}
Output: 8 7 6
Explanation:
Default storage class of variables a, b, c and d is auto. Since it automatic variable it will be sorted in the
stack area of memory. It will store in the stack as
Stack always follows LIFO data structure. In the printf function, name of variables is not
written explicitly. So default output will content of stack which will be in the LIFO order i.e.
8 7 6.
It has two part one for initialize variable another for non-initialize variable. All initialize
variables are more nearer than not initialized variable and vice versa. For example:
Automatic variable a and c has initialized while b has not initialized. Initialize variables are more nearer than
uninitialized variable .They will be stored in the stack. So due to LIFO first output will be 7 then 6 (since a
is more nearer than b with respect to c) then any garbage value will be output which is present in the stack.
2. Data area:
All static and extern variable are stored in the data area. It is permanent memory space and variable will
store in the memory unless and until program end. For example:
#include<stdio.h>
int main(){
9
int i;
for(i=0;i<3;i++){
static int a=5;
printf("%d",a);
}
return 0;
}
Output: 5 6 7
3. Heap area:
This memory area is use to allocate memory dynamically. In c we can allocate the memory space
dynamically by using function malloc and calloc. It always allocates memory in the heap area. Its size is
variable and depends upon free space in the memory.
4. Code area:
Function pointer can only access code area. Size of this area is always fixed and it is read only memory area.
#include <stdio.h>
void main(int count,char * argv[])
{
int i;
FILE *ptr;
char *str;
char ch;
if(count==1)
{
printf("The syntax of the command is incorrect.\n");
}
for(i=1;i<count;i++)
{
ptr=fopen(argv[i],"r");
if(ptr==NULL)
{
printf("The system cannot find the file specified.");
if(count>2)
printf("\nError occurred while procesing : %s.\n",argv[i]);
}
else
{
if(count>2)
{
printf("%s\n\n",argv[i]);
}
while((ch=getc(ptr))!=-1)
10
printf("%c",ch);
}
fclose(ptr);
}
}
Step 2: Save the as open.c (You can give any name) Step 3: Compile and execute the file. Step 4: Write
click on My computer of Window XP operating system and select properties. Step 5: Select Advanced ->
Environment Variables Step 6: You will find following window: Click on new button (Button inside the red
box)
Step 7: Write following: Variable name: path Variable value: c:\tc\bin\open.c (the path where you have
saved)
Step 8: Open command prompt and write open then file name and press enter button.
11
Day 4(Advanced C)
IVT in C:
An interrupt is an exception, a change of the normal progression, or interruption in the normal flow of
program execution. An interrupt is essentially a hardware generated function call. Interrupts are caused by
both internal and external sources. An interrupt causes the normal program execution to halt and for the
interrupt service routine (ISR) to be executed. At the conclusion of the ISR, normal program execution is
resumed at the point where it was last. On the x86 architecture, the Interrupt Vector Table (IVT) is
a table that specifies the addresses of all the 256 interrupt handlers used in real mode. An interrupt vector
table, a concept common across various processor architectures, is a table of interrupt vectors that associates
an interrupt handler with an interrupt request in a machine specific way. A dispatch table is one method of
implementing an interrupt vector table. Most processors have an interrupt vector table (IVT), including chips
from Infineon, Microchip Atmel, Free scale, AMD, Intel, etc.
-keyboard strokes
-1ms clock ticks
-serial data (USART, SPI, TWI)
-analog to digital conversion
The "interrupt acknowledge" method, the external device gives the CPU an interrupt handler number. The
interrupt acknowledge method is used by the Intel Pentium and many older microprocessors. When the CPU
is interrupted by an interrupt, it looks up the interrupt handler in the interrupt vector table, and transfers
control to it.
1. struct BYTEREGS {
unsigned char al, ah, bl, bh;
unsigned char cl, ch, dl, dh;
};
2. struct WORDREGS {
unsigned int ax, bx, cx, dx;
unsigned int si, di, cflag, flags;
};
3. union REGS {
struct WORDREGS x;
struct BYTEREGS h;
};
There is also one very important function int86 () which has been defined in dos.h header file. It is general
8086 software interrupt interface. It will better to explain it by an example.
12
#include <dos.h>
#include <stdio.h>
void main()
{
union REGS i,o;
i.x.ax=1;
int86(0x33,&i,&o);
getch();
}
To write such program you must have one interrupt table. Following table is only small part of interrupt
table.
This table consists for column. They are:
(1) Input
(2) Output
(3) Service number
(4) Purpose
struct WORDREGS {
unsigned int ax, bx, cx, dx;
unsigned int si, di, cflag, flags;
};
And WORDRGS is define in the union REGS
union REGS {
struct WORDREGS x;
struct BYTEREGS h;
};
So to access the structure member ax first declare a variable of REGS i.e. REGS i, o; Note: We generally
use i for input and o for output
To access the ax write i.x.ax (We are using structure variable i because ax is input(See in the interrupt table)
So to show mouse pointer assign the value of service number to it: i.x.ax=1;
To provide this information to microprocessor we use int86 function. It has three parameters
The Interrupt Descriptor Table (IDT) is a data structure used by the x86 architecture to implement
an interrupt vector table. The IDT is used by the processor to determine the correct response
to interrupts and exceptions.
Use of the IDT is triggered by three types of events: hardware interrupts, software interrupts, and processor
exceptions, which together are referred to as "interrupts". The IDT consists of 256 interrupt vectors–the first
32 (0-31 or 00-1F) of which are reserved for processor exceptions.
Real mode
In the 8086 processor, the IDT resides at a fixed location in memory from address 0x0000 to 0x03ff, and
consists of 256 four-byte real mode pointers (256 × 4 = 1024 bytes of memory). In the 80286 and later, the
size and locations of the IDT can be changed in the same way as it is done in protected mode, though it does
not change the format of it. A real mode pointer is defined as a 16-bit segment address and a 16-bit offset
into that segment. A segment address is expanded internally by the processor to 20 bits thus limiting real
mode interrupt handlers to the first 1 megabyte of addressable memory. The first 32 vectors are reserved for
the processor's internal exceptions, and hardware interrupts may be mapped to any of the vectors by way of
a programmable interrupt controller.
A commonly used x86 real mode interrupt is INT 10, the Video BIOS code to handle primitive screen
drawing functions such as pixel drawing and changing the screen resolution.
# include <stdlib.h> # include <stdio.h> # include <conio.h> # include <dos.h>
int main( )
{
unsigned long far* ptrIVT=(unsigned long far*)0x00000000;
unsigned int uiSegment;
unsigned int uiOffset;
int iCount;
FILE* oFile=fopen("IVT.txt","wt");
if(oFile==NULL)
{
clrscr( );
printf("Error : Unable to create IVT.txt file.");
printf("Press any key to exit.");
getch( );
exit(1);
}
fprintf(oFile,"Interrupt Number ---> Vector (Segment:Offset)\n");
fprintf(oFile," ( Hex ) ( Hex ) \n\n");
for(iCount=0;iCount<256;iCount++)
{
uiSegment=FP_SEG(*ptrIVT);
uiOffset=FP_OFF(*ptrIVT);
fprintf(oFile," INT 0x%02X ---> %4X : %X \n",iCount,uiSegment,uiOffset);
ptrIVT++;
}
fclose(oFile);
getch();
return 0; }
14
Day 5(Advanced C)
Introduction to microprocessor
In order to learn programming a microprocessor, first we need to learn how to use registers.
Registers are like utilities of a microprocessor to store data temporarily and manipulate it as per our requirements.
Suppose say if the user wants to add 3 with 2, the user asks the computer to store number 3 in one register and number
2 in more register and then add the contents of the these two registers and the result is placed in another register by the
CPU which is the output that we desire to see. There are four types of registers and are listed below.
General purpose registers: These are used to store temporary data required by the program during its lifecycle. Each
of these registers is 16 bit wide or 2 bytes long.
Segment Registers: To represent a memory address to a microprocessor, there are two terms we need to be aware of:
Example: Suppose say, there is a byte whose value is 'X' that is present on a block of memory whose start address is
0x7c00 and the byte is located at the 10th position from the beginning. In this situation, We represent segment as
0x7c00 and the offset as 10. The absolute address is 0x7c00 + 10.
CS - code segment
SS - stack segment
DS - data segment
ES - extended segment
But there is always a limitation with these registers. You cannot directly assign an address to these registers. What we
can do is, copy the address to a general purpose registers and then copy the address from that register to the segment
registers. Example: To solve the problem of locating byte 'X', we do the following way…
set 0x07c0 * 16 in AX
16
set DS = AX = 0x7c00
set 0x7c00 + 0x0a to ax
There are various addressing modes that we need to understand while writing programs.
Stack Registers:
BP - base pointer
SP - stack pointer
Index Registers:
What is a bit?
In computing, a bit is the smallest unit where data can be stored. Bits store data in the form of binary. Either a 1(On)
or 0(Off).
The registers are further divided as below following left to right order or bits:
AX: The first 8 bits of AX is identified as AL and the last 8 bits is identified as AH
BX: The first 8 bits of BX is identified as BL and the last 8 bits is identified as BH
CX: The first 8 bits of CX is identified as CL and the last 8 bits is identified as CH
DX: The first 8 bits of DX is identified as DL and the last 8 bits is identified as DH
To interrupt the ordinary flow of a program and to process events that require prompt response we use interrupts. The
hardware of a computer provides a mechanism called interrupts to handle events. For example, when a mouse is
moved, the mouse hardware interrupts the current program to handle the mouse movement (to move the mouse cursor,
etc.) Interrupts cause control to be passed to an interrupt handler. Interrupt handlers are routines that process the
interrupt. Each type of interrupt is assigned an integer number. At the beginning of physical memory, a table of
interrupt vectors resides that contain the segmented addresses of the interrupt handlers. The number of interrupt is
essentially an index into this table. We can also called as the interrupt as a service offered by BIOS.
17
A data type is used to identify the characteristic of a data. Various data types are as below.
byte
word
int
ascii
asciz
byte: It is eight bits long. A byte is considered as the smallest unit on a computer onto which data can be stored
through programming.
word: It is a unit of data that is 16 bits long.
What is an int?
An int is a data type that represents data of 32 bits long. Four bytes or two words constitute an int.
What is an ascii?
A data type to represent a group of bytes without a null terminator.
What is an asciz?
A data type to represent a group of bytes terminated with a null character in the end.
/***************************************************************************************
This is a demo ISR to show how to create an ISR in C using the
_interrupt keyword. */
#include "dos.h"
# include <stdio.h>
# include <conio.h>
int main( )
{
unsigned char far* ptrPcType=(unsigned char far*)0xF000FFFE;
clrscr();
printf("Model Identification Code:\n\n");
printf(" Code (Hex) ::: Meanings\n");
printf(" FC : AT\n");
printf(" FE : XT\n");
printf(" FB : XT\n");
printf(" FF : PC\n");
getch( );
return 0;
}
19