You are on page 1of 46

Data Structure, Leftovers, Gotcha and Inline

Assembly

Structures Binary Trees


Linked List Leftovers & Gotcha
Stacks Inline Assembly Code
Queues Hardware interaction
Structure and Union
Struct Char { Union Char {
Char a; Char a;
Int I; Int I;
Float f; Float f;
}; };
Structure
Struct Char {
Char a; x.a
Int I; x.I
}x;
In this case
Struct Char {
Char a;
Int I; x.a
}x __attribute__((packed)); x.I

Padding disabled
Structure: Bit Fields
Struct{
unsigned char ea:1;
unsigned char oct:3;
unsigned char hex:4;
};

7 0
Structure: Nested
Struct{
char name[10];
struct {
int dd;
char mon[4];
int year;
}dob ;
}x;
x.dob.year=2004;

23 21 16
11 8
7 3 0
Union: Nested
Union{
WREG word;
BREG byte;
}x;
x.byte.al=2;
x.word.ax=1234;

15 8
7 0
Linked List
• A chain for data structure to maintain various
information we need at the run time particularly

• Types:
– Singly
– Doubly
– Circular
– Doubly Circular
Linked List
• Development is based on self referenced
structure.
struct slist
{
Int info;
struct slist *next;
};

• Pointers to structure are the most used for


run time link maintenance.
Linked List
Building a Link List

• A code to be Implemented
– Creation
– Deletion
– Traversing and printing
– Searching
Stacks and Queues
• Stacks and queues are two very common and
popular data structures.

• These data structures are often implemented using


arrays since most programming languages provide
array as a predefined data type, and such an
implementation is therefore quite easy.

• However, when implemented as an array these data


structures suffer from the basic limitation.
Stacks and Queues
• A stack is a data structure in which addition of new element or
deletion of existing element always takes place at the same end.

• This end is often known as top of stack.

• This situation can be compared to a stack of plates in a cafeteria


where every new plate added to the stack is added at the top.

• Similarly, every new plate taken off the stack is also from the top
of the stack.
Stacks and Queues
• There are several applications where stack can be put to use.

• For example, recursion, keeping track of function calls,


evaluation of expressions etc.

• Unlike a stack, in a queue the addition of new element takes


place at the end (called rear of queue), whereas deletion takes
place at the other end (called front of queue).

• The following figure shows these two data structures.

• A stack is often called a Last-In-First-Out (LIFO) structure,


whereas a queue is called a First-In-First-Out (FIFO) structure
Stacks and Queues
Binary Trees
• Trees are non-linear data structures.
• In a linked list each node has a link which points to
another node.
• In a tree structure, however, each node may point to
several other nodes (which may then point to several
other nodes, etc.).
• Thus a tree is a very flexible and powerful data
structure that can be used for a wide variety of
applications.
– For example, suppose we wish to use a data structure to
represent a person and all of his or her descendants.
Binary Trees
Binary Trees
•A binary tree is a finite set of elements that is either empty or is
partitioned into three disjoint subsets.

•The first subset contains a single element called the root of the
tree.

•The other two subsets are themselves binary trees, called the
left and right subtrees of the original tree.

•A left or right subtree can be empty. Each element of a binary


tree is called a node of the tree and the tree consists of nine
nodes with A as its root. Its left subtree is rooted at B and its
right subtree is rooted at C . This is indicated by the two
branches emanating from A to B on the left and to C on the right.
The absence of a branch indicates an empty subtree.
Traversal: Binary Trees
• To traverse a nonempty binary tree in preorder , we perform the
following three operations:
Visit the root.
Traverse the left subtree in preorder.
Traverse the right subtree in preorder.

• To traverse a nonempty binary tree in inorder (or symmetric order):


Traverse the left subtree in inorder.
Visit the root.
Traverse the right subtree in inorder.

• To traverse a nonempty binary tree in postorder :


Traverse the left subtree in postorder.
Traverse the right subtree in postorder.
Visit the root.
Binary Trees
• Example List: 20 17 6 8 10 20 7 18 13 12 5 6
Bit Arrays

On this array we may be required to perform the following


operations:

(a) Set a bit (make it 1).


(b) Clear a bit (make it 0).
(c) Test the status of a bit in the array.
(d) Reach the appropriate bit slot in the array.
(e) Generate a bit mask for setting and clearing a bit.
Bit Arrays

We can implement these operations using macros given below:

#define CHARSIZE 8
#define MASK(y) ( 1 << y % CHARSIZE )
#define BITSLOT(y) ( y / CHARSIZE )
#define SET( x, y) ( x[BITSLOT(y)] |= MASK(y) )
#define CLEAR(x, y) ( x[BITSLOT(y)] &= ~MASK(y) )
#define TEST(x, y) ( x[BITSLOT(y)] & MASK(y) )
#define NUMSLOTS(n) ((n + CHARSIZE - 1) / CHARSIZE)
Bit Arrays
Using these macros we can declare an array of 50 bits be saying,
char arr[NUMSLOTS(50)] ;
To set the 20th bit we can say,
SET ( arr, 20 ) ;
And if we are to test the status of 40th bit we may say,
if ( TEST ( arr, 40 ) )
Using bit arrays often results into saving a lot of precious memory.

• For example, the program which implements the generating


prime numbers smaller than 100 requires only 13 bytes.

Had we implemented the same logic using an array of integers


we would have required an array of 100 integers, that is 200
bytes.
Bit Arrays
#include "stdio.h"
#include "string.h"
#define MAX 100
main( )
{
char arr[NUMSLOTS(MAX)] ;
int i, j ;
memset ( arr, 0, NUMSLOTS(MAX) ) ;
for ( i = 2 ; i < MAX ; i++ )
{
if ( !TEST ( arr, i ) )
{
printf ( "\n%d", i ) ;
for ( j = i + i ; j < MAX ; j += i )
SET ( arr, j ) ;
}}}
Complex declaration

int * f (void (*ptr)(int,float,…)

Char * cyz( int (*ptr[12]) (int**, (*p)(char*)),int x )


Gotcha: A must for Programmers

• Static checking in C catches many potential errors at translation time


that might otherwise become run time errors.

• C++ applies even stricter checks to weed out more errors at


translation time. Nonetheless, C and C++ programs can still have
gotchas – constructs that compile and link without diagnostics,
yet produce unintended, and often disastrous, run time results.

• Gotchas also include portability problems whereby a program runs


properly in one environment, yet fails in another.

– This session explains why gotchas are an inherent part of C and C++.
Types of Gotchas

A gotcha occurs when your program compiles and links in a


given environment without diagnostics, and then behaves
badly in one of the following ways:
– The program produces erroneous results.
– The program terminates abnormally.
– The program runs very slowly or runs out of memory.

• A gotcha may also occur when you port seemingly


correct code from one environment to another and then:
– The program no longer compiles or links.
– The program misbehaves as in one of the ways listed above.
Types of Gotchas

A gotcha occurs when your program compiles and links in a


given environment without diagnostics, and then behaves
badly in one of the following ways:
– The program produces erroneous results.
– The program terminates abnormally.
– The program runs very slowly or runs out of memory.

• A gotcha may also occur when you port seemingly


correct code from one environment to another and then:
– The program no longer compiles or links.
– The program misbehaves as in one of the ways listed above.
Categories of Non-Portable Behavior

• Undefined behavior :

– behavior, upon use of an erroneous construct or


erroneous data, for which the language imposes no
requirement.

– Run time diagnosis can be costly, so programs are


allowed to just ignore run time errors.
Categories of Non-Portable Behavior

• Here is a partial list of things a program can do that lead to


undefined behavior
1. Converting an expression to a type that cannot represent the resulting
value.
2. Converting a pointer value to a different pointer type and, in so doing,
producing a result that is incorrectly aligned.
3. Attempting to modify a string literal.
4. Applying the unary * operator to a pointer whose value is invalid or
null.
5. Evaluating a / or % expression whose second operand is zero.
6. Adding an integer to or subtracting it from a pointer to or just beyond
an array element, and producing a result that does not point to or just
beyond an element of the same array.
7. Subtracting one pointer from another, where the pointers do not point
to, or just beyond, elements of the same array.
Categories of Non-Portable Behavior

• Unspecified behavior:

• behavior, of a correct construct and correct data, for which the


language imposes no requirement.

• For example, the following program behaviors are unspecified:


1. The manner in which a program initializes statically allocated objects.
2. The consequences of returning something other than an integer from
main.
3. The value of padding bytes when storing values in structures or
unions.
4. The order in which a function call evaluates the function designator
and arguments.
5. The order in which an expression evaluates sub-expressions and
executes side effects,
Program-1

1: #include <stdio.h> 11: f(n++, n);


2: 12: n = 0;
3: void f(int i, int j) 13: f(n, n++);
4: {
14: n = 0;
5: printf("i = %d; j = %d\n", i, j);
15: f(n, ++n);
6: }
7: 16: return 0;
8: int main() 17: }
9: {
10: int n = 0;
Who is right?
When compiled with some compilers, the program produces the
following output:
i = 0; j = 0
i = 1; j = 0
i = 1; j = 1
When compiled with at least one other compiler, it produces
something different:
i = 0; j = 0
i = 0; j = 0
i = 1; j = 1
Who is right?
The order of evaluation of the function designator, the arguments, and
sub-expressions within the arguments is unspecified, but there is a
sequence point before the actual call.

In the function call expression f(n++, n), f is the function designator,


n++ is the first argument, and n is the second argument. They are all
expressions.

Since the order of evaluation for the function designator and


arguments is unspecified, the compiler is free to interpret:
10: int n = 0;
11: f(n++, n);
in a few different ways:
Who is right?
It can evaluate the arguments from right to left. In that case, the
both arguments will be 0.
It can evaluate the arguments from left to right, and delay the post-
increment on the first argument until after evaluating the second
argument. In that case, both arguments will also be 0.
It can evaluate the arguments from left to right, and perform the
post-increment on the first argument before evaluating the second
argument.
In that case, the first argument will be 0 and the second will be 1.
Although the order of argument evaluation is unspecified, there is a
sequence point before the actual call.
Sequence points simply means that the compiler cannot delay the
post-increment until after the call.
Guideline: Avoiding writing function calls in which the behavior
depends on the order of evaluation.
Sequence Points
A sequence point is any point in a program’s execution wherein all side
effects of previous evaluations are complete and no side effects of
subsequent evaluations have started.

For example, as mentioned earlier, there’s a sequence point after the


evaluation of the function designator and arguments in a function call
and before the call actually occurs.

In a call such as f(i++, j++), the C standard doesn’t tell you whether the
program will increment i before or after incrementing j, but it does tell
you that both i and j will be incremented before the program arrives at f.
Precedence vs. Evaluation Order

1: #include <stdio.h> 14: 15: printf("a[%d] = %d\n", i, a[i]);


2: 16: printf("2nd pass...\n");
3: int main()
4: { 17: for (i = 0; i < n; ++i)
5: enum { n = 3 }; 18: a[i] = -1;
6: int a[n];
7: int i; 19: i = 0;
8: printf("1st pass...\n"); 20: while (i < n)
9: for (i = 0; i < n; ++i)
10: a[i] = -1; 21: a[i] = i++;
11: i = 0; 22: for (i = 0; i < n; ++i)
12: while (i < n) 23: printf("a[%d] = %d\n", i, a[i]);
13: a[i++] = i; 24: return 0;
for (i = 0; i < n; ++i) 25: }
Precedence vs. Evaluation Order
When compiled with some When compiled with at least one
compilers, the program other compiler, the program
produces the following produces a rather different
output: result:
1st pass... 1st pass...
a[0] = 0 a[0] = 1
a[1] = 1 a[1] = 2
a[2] = 2 a[2] = 3
2nd pass... 2nd pass...
a[0] = 0 a[0] = -1
a[1] = 1 a[1] = 0
a[2] = 2 a[2] = 1
This compiler is compiling the program correctly. It’s just exercising the latitude granted
by the C standard.
Operator precedence influences, but does not completely dictate, the evaluation order of
sub-expressions within an expression.
Inline Assembly

• The most used in Embedded C programs


• This is processor specific code
• Every assembler has their own way of interaction
• It’s the part of c code and provides interaction with
hardware to a extent
• Does not supports data handling as pure assembly code
• Though a set of calls can made via c to external asm codes
• Can be useful in lot of circumstances ?
• Different in assembler implementation:
MASM,TASM,BASM,NASM etc
Inline Assembly

#include <conio.h>
void main( ) printf ( "addition result = %d\n", result );
{ _asm {
int result ; mov ax, 10
clrscr( ) ; mov bx, 4
asm mov ax, 5 sub ax, bx
asm mov bx, 7 mov result, ax
asm add ax, bx printf ( "Subtraction result = %d\n", result ) ;
asm mov result, ax }
Inline Assembly

• As emphasized earlier, Assembly is useful in following


situations:

• (a) Increasing speed of critical operations


• (b) Performing operations that cannot be done through C
• (c) Make the code compact
Inline Assembly: 32 bit Application

• Works the same way as in a difference of instruction set


• Plays vital role in terms of size and execution

int num;
_asm{
cli
mov eax,num
add eax,0x1234
sti ;
}
………….
Accessing Hardware

To Do
– Understand the 16 bit layout
– Dos memory map
– Accessing external Segments
• Far
• huge
– A reset code
– A security code
– Screen handling
– Checking memory parameters
– Accessing keyboard
The system memory layout

PC/XT /AT Extended memory

10000H
Reserved for ROM BIOS
E0000H
Reserved for Installable ROM
C0000H Video Buffer

A0000H
TP of DOS
User program and data
Resident Portion of DOS
00500H DATA AREA ROM BIOS BASIC
DATA AREA FOR ROM
00400H IVT

00000H
Some Address to look at

• Video ram 0xB8000 (void)


• Keyboard: 0x417 (char)
• Base Memory: 0x413(char)
Try some code to test these location

You might also like